ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 판다스
    Python 2023. 11. 14. 17:15

    고수준의 자료구조와 파이썬을 통한 빠르고 쉬운 데이터 분석 도구 제공, 넘파이의 배열 기반의 계산 스타일을 많이 차용함

    판다스, 넘파이의 가장 큰 차이점은 판다스는 표 형식의 데이터나 다양한 형태의 데이터를 다루는 데 초점을 맞춰 설계, 넘파이는 단일 산술 배열 데이터를 다루는 데 특화되어 있음

     

    넘파이와 판다스의 import 컨벤션

    In [1]: import numpy as np
    
    In [2]: import pandas as pd

     

    Series와 DataFrame은 로컬 네임스페이스로 임포트하는 것이 편하므로 다음과 같이 사용

    In [3]: from pandas import Series, DataFrame

     

    판다스 자료구조 소개

     

    두 가지 자료구조 Series와 DataFrame

     

    Series

    일련의 객체를 담을 수 있는 1차원 배열 같은 자료구조(어떤 넘파이 자료형이라도 담을 수 있음)

    그리고 색인(index)라 하는 배열의 데이터와 연관된 이름을 가짐, 가장 간단한 Series 객체는 배열 데이터로부터 생성

    In [4]: obj = pd.Series([4, 7, -5, 3])
    
    In [5]: obj
    Out[5]:
    0    4
    1    7
    2   -5
    3    3
    dtype: int64

    왼쪽에는 인덱스, 오른쪽에는 인덱스 값을 보여 줌

    데이터의 인덱스를 지정하지 않으면 기본 인덱스인 0부터 N-1까지의 숫자 표시

    Series의 배열과 색인 객체는 각각 array와 index 속성을 통해 얻을 수 있음

    In [6]: obj.array
    Out[6]:
    <PandasArray>
    [4, 7, -5, 3]
    Length: 4, dtype: int64
    
    In [7]: obj.index
    Out[7]: RangeIndex(start=0, stop=4, step=1)

    일반적으로 .array 속성의 결과는 넘파이 배열을 감싸는 PandasArray

     

    각 데이터를 지칭하는 색인을 지정해 Series 객체를 생성

    In [8]: obj2 = pd.Series([4, 7, -5, 3], index=['d', 'b', 'a', 'c'])
    
    In [9]: obj2
    Out[9]:
    d    4
    b    7
    a   -5
    c    3
    dtype: int64
    
    In [10]: obj2.index
    Out[10]: Index(['d', 'b', 'a', 'c'], dtype='object')

    넘파이 배열과 비교하면, 단일 값을 선택하거나 여러 값을 선택할 때 색인으로 레이블(라벨, label) 사용 가능

    In [11]: obj2['a']
    Out[11]: -5
    
    In [12]: obj2['d']=6
    
    In [13]: obj2
    Out[13]:
    d    6
    b    7
    a   -5
    c    3
    dtype: int64
    
    In [14]: obj2[['c', 'a', 'd']]
    Out[14]:
    c    3
    a   -5
    d    6
    dtype: int64

     

    불리언 배열을 사용해 값을 걸러내거나 스칼라 곱셈 수행, 수학 함수를 적용하는 등 넘파이 배열 연산을 수행해도 색인과 값 연결은 유지됨

    In [15]: obj2[obj2 > 0]
    Out[15]:
    d    6
    b    7
    c    3
    dtype: int64
    
    In [16]: obj2 * 2
    Out[16]:
    d    12
    b    14
    a   -10
    c     6
    dtype: int64
    
    In [17]: import numpy as np
    
    In [18]: np.exp(obj2)
    Out[18]:
    d     403.428793
    b    1096.633158
    a       0.006738
    c      20.085537
    dtype: float64

    Series를 이해하는 다른 방법은 고정 길이의 정렬된 딕셔너리라고 생각하는 것, 파이썬의 딕셔너리와 비슷함

    파이썬의 딕셔너리가 필요한 곳에 Series 객체를 사용할 수 있음

    In [19]: 'b' in obj2
    Out[19]: True
    
    In [20]: 'e' in obj2
    Out[20]: False

     

    파이썬의 딕셔너리에 데이터를 저장해야 한다면 파이썬 딕셔너리 객체로부터 Series 객체를 생성할 수도 있음

    In [21]: sdata = {'Ohio': 35000, 'Texas': 71000, 'Oregon': 16000, 'Utah': 5000}
    
    In [22]: obj3 = pd.Series(sdata)
    
    In [23]: obj3
    Out[23]:
    Ohio      35000
    Texas     71000
    Oregon    16000
    Utah       5000
    dtype: int64

     

    to_dict 메서드를 사용해 Series를 다시 딕셔너리로 변환 가능

    In [24]: obj3.to_dict()
    Out[24]: {'Ohio': 35000, 'Texas': 71000, 'Oregon': 16000, 'Utah': 5000}

     

    딕셔너리 객체만 가지고 Series 객체를 생성하면 생성된 Series 객체의 색인은 딕셔너리의 key 메서드에서 반환하는 키의 값 순서대로 들어감, 색인을 직접 지정하고 싶다면 원하는 순서대로 색인을 넘길 수도 있음

    In [25]: states = ['California', 'Ohio', 'Oregon', 'Texas']
    
    In [26]: obj4 = pd.Series(sdata, index=states)
    
    In [27]: obj4
    Out[27]:
    California        NaN
    Ohio          35000.0
    Oregon        16000.0
    Texas         71000.0
    dtype: float64

    위 예제를 보면 sdata에 있는 값 중 3개만 확인할 수 있음, California에 대한 값을 찾을 수 없기 때문 > 위 값은 NaN으로 표시

    판다스에서는 누락된 값 또는 NA 값으로 취급

    누락된 데이터 지칭할 때 '누락되다', 'NA', 'null' 표현 사용

     

    누락된 데이터 찾을 때 사용하는 isnull, notnull 함수

    In [28]: pd.isna(obj4)
    Out[28]:
    California     True
    Ohio          False
    Oregon        False
    Texas         False
    dtype: bool
    
    In [29]: pd.notna(obj4)
    Out[29]:
    California    False
    Ohio           True
    Oregon         True
    Texas          True
    dtype: bool
    In [30]: obj4.isna()
    Out[30]:
    California     True
    Ohio          False
    Oregon        False
    Texas         False
    dtype: bool

     

    Series의 유용한 기능은 산술 연산에서 색인과 레이블로 자동 정렬하는 기능

    In [31]: obj3
    Out[31]:
    Ohio      35000
    Texas     71000
    Oregon    16000
    Utah       5000
    dtype: int64
    
    In [32]: obj4
    Out[32]:
    California        NaN
    Ohio          35000.0
    Oregon        16000.0
    Texas         71000.0
    dtype: float64
    
    In [33]: obj3 + obj4
    Out[33]:
    California         NaN
    Ohio           70000.0
    Oregon         32000.0
    Texas         142000.0
    Utah               NaN
    dtype: float64

     

    Series 객체와 색인은 모두 name 속성을 가지고, 이 속성은 판다스의 다른 기능들과 통합되어 있음

    In [34]: obj4.name = 'population'
    
    In [35]: obj4.index.name = 'state'
    
    In [36]: obj4
    Out[36]:
    state
    California        NaN
    Ohio          35000.0
    Oregon        16000.0
    Texas         71000.0
    Name: population, dtype: float64
    
    # 대입으로 Series의 색인 변경 o
    In [37]: obj
    Out[37]:
    0    4
    1    7
    2   -5
    3    3
    dtype: int64
    
    In [38]: obj.index = ['Bob', 'Steve', 'Jeff', 'Ryan']
    
    In [39]: obj
    Out[39]:
    Bob      4
    Steve    7
    Jeff    -5
    Ryan     3
    dtype: int64

     

    DataFrame

    표 같은 스프레드시트 형식의 자료구조, 여러 개의 열이 있고 서로 다른 종류의 값(숫자, 문자열, 불리언)을 담을  수 있음

    행과 열에 대한 색인을 가지며, 색인의 모양이 같은 Series 객체를 담고 있는 파이썬 딕셔너리로 생각하면 됨

    가장 흔한 방법은 동일한 길이의 리스트에 담긴 딕셔너리를 이용하거나 넘파이 배열을 이용하는 방법

    In [40]: data = {'state':['Ohio', 'Ohio', 'Ohio', 'Nevada', 'Nevada', 'Nevada'],
        ...:         'year': [2000, 2001, 2002, 2001, 2002, 2003],
        ...:         'pop': [1.5, 1.7, 3.6, 2.4, 2.9, 3.2]}
    
    In [41]: frame = pd.DataFrame(data)
    
    In [42]: frame
    Out[42]:
        state  year  pop
    0    Ohio  2000  1.5
    1    Ohio  2001  1.7
    2    Ohio  2002  3.6
    3  Nevada  2001  2.4
    4  Nevada  2002  2.9
    5  Nevada  2003  3.2

     

    큰 DataFrame을 다룰 때는 head 메서드를 이용해 처음 5개 행만 출력, 마지막 5개 행도 출력 가능

    In [43]: frame.head()
    Out[43]:
        state  year  pop
    0    Ohio  2000  1.5
    1    Ohio  2001  1.7
    2    Ohio  2002  3.6
    3  Nevada  2001  2.4
    4  Nevada  2002  2.9
    
    In [44]: frame.tail()
    Out[44]:
        state  year  pop
    1    Ohio  2001  1.7
    2    Ohio  2002  3.6
    3  Nevada  2001  2.4
    4  Nevada  2002  2.9
    5  Nevada  2003  3.2
    
    In [45]: frame.head(2)
    Out[45]:
      state  year  pop
    0  Ohio  2000  1.5
    1  Ohio  2001  1.7
    
    In [46]: frame.tail(2)
    Out[46]:
        state  year  pop
    4  Nevada  2002  2.9
    5  Nevada  2003  3.2

    개수 지정이 가능하지만 보통 지정하지 x

     

    columns을 원하는 순서대로 지정하면 해당 순서로 정렬된 DataFrame 객체가 생성됨

    In [47]: pd.DataFrame(data, columns=['year', 'state', 'pop'])
    Out[47]:
       year   state  pop
    0  2000    Ohio  1.5
    1  2001    Ohio  1.7
    2  2002    Ohio  3.6
    3  2001  Nevada  2.4
    4  2002  Nevada  2.9
    5  2003  Nevada  3.2

     

    딕셔너리에 없는 값을 columns에 넘기면 결과에 결측치(missing value)가 표시

    In [48]: frame2 = pd.DataFrame(data, columns=['year', 'state', 'pop', 'debt'])
    
    In [49]: frame2
    Out[49]:
       year   state  pop debt
    0  2000    Ohio  1.5  NaN
    1  2001    Ohio  1.7  NaN
    2  2002    Ohio  3.6  NaN
    3  2001  Nevada  2.4  NaN
    4  2002  Nevada  2.9  NaN
    5  2003  Nevada  3.2  NaN
    
    In [50]: frame2.columns
    Out[50]: Index(['year', 'state', 'pop', 'debt'], dtype='object')

     

    DataFrame의 열은 Series처럼 딕셔너리 형식의 표기법이나 점 표기법으로 접근할 수 있음

    In [51]: frame2['state']
    Out[51]:
    0      Ohio
    1      Ohio
    2      Ohio
    3    Nevada
    4    Nevada
    5    Nevada
    Name: state, dtype: object
    
    In [52]: frame2.year
    Out[52]:
    0    2000
    1    2001
    2    2002
    3    2001
    4    2002
    5    2003
    Name: year, dtype: int64

    반환된 Series 객체가 DataFrame과 같은 색인을 가지면 알맞은 값으로 name 속성이 채워짐

    iloc이나 loc 같은 몇 가지 속성을 사용해 위치나 이름으로 행에 접근할 수 있음

    In [53]: frame2.loc[1]
    Out[53]:
    year     2001
    state    Ohio
    pop       1.7
    debt      NaN
    Name: 1, dtype: object
    
    In [54]: frame2.iloc[2]
    Out[54]:
    year     2002
    state    Ohio
    pop       3.6
    debt      NaN
    Name: 2, dtype: object
    
    In [55]: frame2
    Out[55]:
       year   state  pop debt
    0  2000    Ohio  1.5  NaN
    1  2001    Ohio  1.7  NaN
    2  2002    Ohio  3.6  NaN
    3  2001  Nevada  2.4  NaN
    4  2002  Nevada  2.9  NaN
    5  2003  Nevada  3.2  NaN

     

    대입으로 열 수정 가능 ex) 현재 비어 있는 debt 열에 스칼라 값이나 배열의 값을 대입할 수 있음

    In [56]: frame2['debt'] = 16.5
    
    In [57]: frame2
    Out[57]:
       year   state  pop  debt
    0  2000    Ohio  1.5  16.5
    1  2001    Ohio  1.7  16.5
    2  2002    Ohio  3.6  16.5
    3  2001  Nevada  2.4  16.5
    4  2002  Nevada  2.9  16.5
    5  2003  Nevada  3.2  16.5
    
    In [58]: frame2['debt'] = np.arange(6.)
    
    In [59]: frame2
    Out[59]:
       year   state  pop  debt
    0  2000    Ohio  1.5   0.0
    1  2001    Ohio  1.7   1.0
    2  2002    Ohio  3.6   2.0
    3  2001  Nevada  2.4   3.0
    4  2002  Nevada  2.9   4.0
    5  2003  Nevada  3.2   5.0

     

    리스트나 배열을 열에 대입할 때 대입하려는 값의 길이가 DataFrame의 길이와 동일해야 함

    Series를 대입하면 DataFrame의 색인에 따라 값이 대입되며 존재하지 않는 색인에는 결측치가 대입됨

    In [60]: val = pd.Series([-1.2, -1.5, -1.7], index=['two', 'four', 'five'])
    
    In [61]: frame2['debt'] = val
    
    In [62]: frame2
    Out[62]:
       year   state  pop  debt
    0  2000    Ohio  1.5   NaN
    1  2001    Ohio  1.7   NaN
    2  2002    Ohio  3.6   NaN
    3  2001  Nevada  2.4   NaN
    4  2002  Nevada  2.9   NaN
    5  2003  Nevada  3.2   NaN

     

    존재하지 않는 열을 대입할 경우 새로운 열이 생성됨

    파이썬 딕셔너리처럼 del 예약어를 사용해 열 삭제 가능

    예시로 state열의 값이 'Ohio'인지 검사한 결과를 불리언 값으로 나타내는 새로운 열(eastern) 만들기

    In [63]: frame2['eastern'] = frame2['state'] == 'Ohio'
    
    In [64]: frame2
    Out[64]:
       year   state  pop  debt  eastern
    0  2000    Ohio  1.5   NaN     True
    1  2001    Ohio  1.7   NaN     True
    2  2002    Ohio  3.6   NaN     True
    3  2001  Nevada  2.4   NaN    False
    4  2002  Nevada  2.9   NaN    False
    5  2003  Nevada  3.2   NaN    False
    
    In [65]: frame2['eastern']
    Out[65]:
    0     True
    1     True
    2     True
    3    False
    4    False
    5    False
    Name: eastern, dtype: bool
    
    In [66]: frame2.eastern
    Out[66]:
    0     True
    1     True
    2     True
    3    False
    4    False
    5    False
    Name: eastern, dtype: bool

    del 예약어로 새롭게 만든 열 삭제

    In [67]: frame2
    Out[67]:
       year   state  pop  debt  eastern
    0  2000    Ohio  1.5   NaN     True
    1  2001    Ohio  1.7   NaN     True
    2  2002    Ohio  3.6   NaN     True
    3  2001  Nevada  2.4   NaN    False
    4  2002  Nevada  2.9   NaN    False
    5  2003  Nevada  3.2   NaN    False
    
    In [68]: del frame2['eastern']
    
    In [69]: frame2
    Out[69]:
       year   state  pop  debt
    0  2000    Ohio  1.5   NaN
    1  2001    Ohio  1.7   NaN
    2  2002    Ohio  3.6   NaN
    3  2001  Nevada  2.4   NaN
    4  2002  Nevada  2.9   NaN
    5  2003  Nevada  3.2   NaN
    
    In [70]: frame2.columns
    Out[70]: Index(['year', 'state', 'pop', 'debt'], dtype='object')

     

    DataFrame의 색인을 이용해 얻은 열은 내부 데이터에 대한 뷰일 뿐 복사가 이루어지지 않음

    > 이렇게 얻은 Series 객체에 대한 변경은 실제 DataFrame에 반영됨, 복사본이 필요할 때는 Series의 copy 메서드 이용

     

    중첩된 딕셔너리를 이용해 데이터 생성할 수 있음

    In [71]: populations = {'Ohio': {2000: 1.5, 2001: 1.7, 2002: 3.6},
        ...:                'Nevada': {2001: 2.4, 2002: 2.9}}
    
    In [72]: frame3 = pd.DataFrame(populations)
    
    In [73]: frame3
    Out[73]:
          Ohio  Nevada
    2000   1.5     NaN
    2001   1.7     2.4
    2002   3.6     2.9

    중첩된 딕셔너리를 DataFrame에 넘기면 바깥에 있는 딕셔너리의 키가 열이 되고 안에 있는 키는 행이 됨

     

    넘파이 배열과 유사한 문법으로 데이터 전치

    In [73]: frame3
    Out[73]:
          Ohio  Nevada
    2000   1.5     NaN
    2001   1.7     2.4
    2002   3.6     2.9
    
    In [74]: frame3.T
    Out[74]:
            2000  2001  2002
    Ohio     1.5   1.7   3.6
    Nevada   NaN   2.4   2.9

    열의 자료형이 모두 같지 않을 경우에 데이터를 전치하게 되면 이전 자료혀에 대한 정보가 유실될 수 있음

    > 그런 경우 열은 순수 파이썬 객체의 배열이 됨

     

    중첩된 딕셔너리를 이용해 DataFrame을 생성하면 안쪽에 있는 딕셔너리 값은 키의 값별로 조합되어 결과의 색인이 됨

    색인을 직접 지정하면 지정된 색인으로 DataFrame 생성

    In [75]: pd.DataFrame(populations, index=[2001, 2002, 2003])
    Out[75]:
          Ohio  Nevada
    2001   1.7     2.4
    2002   3.6     2.9
    2003   NaN     NaN
    
    In [76]: frame3
    Out[76]:
          Ohio  Nevada
    2000   1.5     NaN
    2001   1.7     2.4
    2002   3.6     2.9

     

    Series 객체를 담고 있는 딕셔너리 데이터도 동일한 방식으로 취급됨

    In [77]: pdata = {'Ohio': frame3['Ohio'][:-1],
        ...:          'Nevada': frame3['Nevada'][:2]}
    
    In [78]: pd.DataFrame(pdata)
    Out[78]:
          Ohio  Nevada
    2000   1.5     NaN
    2001   1.7     2.4

     

    DataFrame의 색인과 열에 name 속성이 설정되어 있다면 이 정보도 함께 출력됨

    In [79]: frame3
    Out[79]:
          Ohio  Nevada
    2000   1.5     NaN
    2001   1.7     2.4
    2002   3.6     2.9
    
    In [80]: frame3.index.name = 'year'
    
    In [81]: frame3.columns.name = 'state'
    
    In [82]: frame3
    Out[82]:
    state  Ohio  Nevada
    year
    2000    1.5     NaN
    2001    1.7     2.4
    2002    3.6     2.9

     

    Series와 달리 DataFrame에는 name 속성이 없음

    DataFrame의 to_numpy 메서드는 DataFrame에 포함된 데이터를 2차원 형태의 ndarray로 반환

    In [83]: frame3
    Out[83]:
    state  Ohio  Nevada
    year
    2000    1.5     NaN
    2001    1.7     2.4
    2002    3.6     2.9
    
    In [84]: frame3.to_numpy()
    Out[84]:
    array([[1.5, nan],
           [1.7, 2.4],
           [3.6, 2.9]])
    
    In [85]: frame2
    Out[85]:
       year   state  pop  debt
    0  2000    Ohio  1.5   NaN
    1  2001    Ohio  1.7   NaN
    2  2002    Ohio  3.6   NaN
    3  2001  Nevada  2.4   NaN
    4  2002  Nevada  2.9   NaN
    5  2003  Nevada  3.2   NaN
    
    In [87]: frame2.to_numpy()
    Out[87]:
    array([[2000, 'Ohio', 1.5, nan],
           [2001, 'Ohio', 1.7, nan],
           [2002, 'Ohio', 3.6, nan],
           [2001, 'Nevada', 2.4, nan],
           [2002, 'Nevada', 2.9, nan],
           [2003, 'Nevada', 3.2, nan]], dtype=object)

     

    색인 객체

    판다스의 색인 객체는 축 레이블(DataFrame의 열 이름 포함)과 다른 메타데이터(축의 이름 등)를 저장하는 객체

    Series나 DataFrame 객체를 생성할 대 사용하는 배열이나 다른 순차적인 레이블은 내부적으로 색인으로 변환됨

    In [88]: obj = pd.Series(np.arange(3), index=['a', 'b', 'c'])
    
    In [89]: index = obj.index
    
    In [90]: index
    Out[90]: Index(['a', 'b', 'c'], dtype='object')
    
    In [91]: index[1:]
    Out[91]: Index(['b', 'c'], dtype='object')
    
    # 색인 객체 변경 불가능
    In [92]: index[1] = 'd'    # TypeError
    ---------------------------------------------------------------------------
    TypeError                                 Traceback (most recent call last)
    Cell In[92], line 1
    ----> 1 index[1] = 'd'
    
    File ~\anaconda3\Lib\site-packages\pandas\core\indexes\base.py:5157,
    in Index.__setitem__(self, key, value)
       5155 @final
       5156 def __setitem__(self, key, value):
    -> 5157     raise TypeError("Index does not support mutable operations")
    
    TypeError: Index does not support mutable operations

    불변성 덕분에 자료구조 사이에서 색인을 안전하게 공유 가능

    In [93]: labels = pd.Index(np.arange(3))
    
    In [94]: labels
    Out[94]: Index([0, 1, 2], dtype='int32')
    
    In [95]: obj2 = pd.Series([1.5, -2.5, 0], index=labels)
    
    In [96]: obj2
    Out[96]:
    0    1.5
    1   -2.5
    2    0.0
    dtype: float64
    
    In [97]: obj2.index is labels
    Out[97]: True

     

    배열과 유사하게 Index 객체도 고정된 크기로 작동

    In [99]: frame3.columns
    Out[99]: Index(['Ohio', 'Nevada'], dtype='object', name='state')
    
    In [100]: 'Ohio' in frame3.columns
    Out[100]: True
    
    In [101]: 2003 in frame3.index
    Out[101]: False

     

    파이썬의 집합과는 달리 판다스의 색인은 중복되는 값 허용

    In [102]: pd.Index(['foo', 'foo', 'bar', 'bar'])
    Out[102]: Index(['foo', 'foo', 'bar', 'bar'], dtype='object')

     

    핵심 기능

     

    재색인

    판다스 객체의 중요한 기능 중 하나 reindex, 새로운 색인에 적합하도록 객체를 새로 생성하는 기능

    In [103]: obj = pd.Series([4.5, 7.2, -5.3, 3.6], index=['d', 'b', 'a', 'c'])
    
    In [104]: obj
    Out[104]:
    d    4.5
    b    7.2
    a   -5.3
    c    3.6
    dtype: float64
    
    # reindex를 호출하면 데이터를 새로운 색인에 맞게 재배열하고,
    # 존재하지 않는 색인값이 있다면 비어 있는 값(NaN)을 새로 추가함
    
    In [105]: obj2 = obj.reindex(['a', 'b', 'c', 'd', 'e'])
    
    In [106]: obj2
    Out[106]:
    a   -5.3
    b    7.2
    c    3.6
    d    4.5
    e    NaN
    dtype: float64

    값을 할당하지 않아도 코드는 실행되지만, 값을 사용하려면 할당해서 써야 함

     

    시계열 같은 순차적인 데이터를 재색인할 때, 값을 보간하거나 채워 넣어야 할 경우, method 옵션을 이용해 해결할 수 있으며

    ffill 같은 메서드를 이용해 누락된 값을 직전의 값으로 채워 넣을 수 있음

    In [107]: obj3 = pd.Series(['blue', 'purple', 'yellow'], index=[0, 2, 4])
    
    In [108]: obj3
    Out[108]:
    0      blue
    2    purple
    4    yellow
    dtype: object
    
    In [109]: obj3.reindex(np.arange(6), method='ffill')
    Out[109]:
    0      blue
    1      blue
    2    purple
    3    purple
    4    yellow
    5    yellow
    dtype: object

     

    DataFrame에 대한 reindex는 행(색인), 열 또는 둘 다 변경 가능, 순서만 전달하면 행이 재색인됨

    In [110]: frame = pd.DataFrame(np.arange(9).reshape((3, 3)),
         ...:                      index=['a', 'c', 'd'],
         ...:                      columns=['Ohio', 'Texas', 'California'])
    
    In [111]: frame
    Out[111]:
       Ohio  Texas  California
    a     0      1           2
    c     3      4           5
    d     6      7           8
    
    In [112]: frame2 = frame.reindex(index=['a', 'b', 'c', 'd'])
    
    In [113]: frame2
    Out[113]:
       Ohio  Texas  California
    a   0.0    1.0         2.0
    b   NaN    NaN         NaN
    c   3.0    4.0         5.0
    d   6.0    7.0         8.0

     

    열은 columns 예약어를 사용해 재색인

    In [111]: frame
    Out[111]:
       Ohio  Texas  California
    a     0      1           2
    c     3      4           5
    d     6      7           8
    
    In [114]: states = ['Texas', 'Utah', 'California']
    
    In [115]: frame.reindex(columns=states)
    Out[115]:
       Texas  Utah  California
    a      1   NaN           2
    c      4   NaN           5
    d      7   NaN           8
    
    # 다른 방법은 새로운 축 레이블을 인수로 넘긴 후 axis 키워드를 사용해 재색인할 축을 지정하는 것
    
    In [118]: frame
    Out[118]:
       Ohio  Texas  California
    a     0      1           2
    c     3      4           5
    d     6      7           8
    
    In [117]: frame.reindex(states, axis='columns')
    Out[117]:
       Texas  Utah  California
    a      1   NaN           2
    c      4   NaN           5
    d      7   NaN           8

     

     

    + loc 연산자를 이용해 재색인할 수 있음, 이 방법은 모든 새로운 색인 레이블이 DataFrame에 존재하는 경우에만 작동

    (reindex 함수는 새로운 레이블에 대한 결측치 삽입)

    In [120]: frame
    Out[120]:
       Ohio  Texas  California
    a     0      1           2
    c     3      4           5
    d     6      7           8
    
    In [121]: frame.loc[ ['a', 'd', 'c'] ]    # 인덱스(행축)만 재색인
    Out[121]:
       Ohio  Texas  California
    a     0      1           2
    d     6      7           8
    c     3      4           5
    
    In [122]: frame.loc[['a', 'd', 'c'], ['California', 'Texas']]
    Out[122]:
       California  Texas
    a           2      1
    d           8      7
    c           5      4

     

    하나의 행이나 열 삭제

    삭제하려는 축이 제외된 리스트를 이미 가지고 있거나, 색인 배열을 갖고 있다면 reindex 메서드나 .loc 기반의 색인을 이용할 수 있으므로 행이나 열을 쉽게 삭제할 수 있으나 이 방법은 데이터의 모양을 변경하는 작업 필요

    drop 메서드를 사용하면 선택한 값들이 삭제된 새로운 객체를 얻을 수 있음

    In [123]: obj = pd.Series(np.arange(5.), index=['a', 'b', 'c', 'd', 'e'])
    
    In [124]: obj
    Out[124]:
    a    0.0
    b    1.0
    c    2.0
    d    3.0
    e    4.0
    dtype: float64
    
    In [125]: new_obj = obj.drop('c')
    
    In [126]: new_obj
    Out[126]:
    a    0.0
    b    1.0
    d    3.0
    e    4.0
    dtype: float64
    
    In [127]: obj.drop(['d', 'c'])
    Out[127]:
    a    0.0
    b    1.0
    e    4.0
    dtype: float64

     

    DataFrame에서는 행과 열 모두에서 색인값을 삭제할 수 있음

    In [128]: data = pd.DataFrame(np.arange(16).reshape((4, 4)),
         ...:                     index=['Ohio', 'Colorado', 'Utah', 'New York'],
         ...:                     columns=['one', 'two', 'three', 'four'])
    
    In [129]: data
    Out[129]:
              one  two  three  four
    Ohio        0    1      2     3
    Colorado    4    5      6     7
    Utah        8    9     10    11
    New York   12   13     14    15

     

    drop 함수 인수로 삭제하고 싶은 행 이름을 넘기면 해당 행의 값 모두 삭제, 열의 레이블을 삭제하려면 columns 키워드 사용

    In [130]: data.drop(index=['Colorado', 'Ohio'])
    Out[130]:
              one  two  three  four
    Utah        8    9     10    11
    New York   12   13     14    15
    
    In [131]: data.drop(columns=['two'])
    Out[131]:
              one  three  four
    Ohio        0      2     3
    Colorado    4      6     7
    Utah        8     10    11
    New York   12     14    15

     

    열의 값을 삭제할 때는 넘파이처럼 axis=1 또는 axis='columns'를 인수로 넘겨주면 됨

    In [132]: data.drop('two', axis=1)
    Out[132]:
              one  three  four
    Ohio        0      2     3
    Colorado    4      6     7
    Utah        8     10    11
    New York   12     14    15
    
    In [133]: data.drop(['two', 'four'], axis='columns')
    Out[133]:
              one  three
    Ohio        0      2
    Colorado    4      6
    Utah        8     10
    New York   12     14

     

    색인하기, 선택하기, 거르기

    Series의 색인(obj[...])은 넘파이 배열의 색인과 유사하게 작동하지만 정수가 아니어도 된다는 점이 다름

    In [135]: obj = pd.Series(np.arange(4.), index=['a', 'b', 'c', 'd'])
    
    In [136]: obj
    Out[136]:
    a    0.0
    b    1.0
    c    2.0
    d    3.0
    dtype: float64
    
    In [137]: obj['b']
    Out[137]: 1.0
    
    In [138]: obj[1]
    Out[138]: 1.0
    
    In [139]: obj[2:4]
    Out[139]:
    c    2.0
    d    3.0
    dtype: float64
    
    In [140]: obj[['b', 'a', 'd']]
    Out[140]:
    b    1.0
    a    0.0
    d    3.0
    dtype: float64
    
    In [141]: obj[[1, 3]]
    Out[141]:
    b    1.0
    d    3.0
    dtype: float64
    
    In [142]: obj[obj < 2]
    Out[142]:
    a    0.0
    b    1.0
    dtype: float64

     

    특수 연산자인 loc을 이용하는 방식이 더 선호됨

    In [143]: obj.loc[['b', 'a', 'd']]
    Out[143]:
    b    1.0
    a    0.0
    d    3.0
    dtype: float64

     []로 선택하는 경우 정수의 처리 방식이 다르기 때문에 loc를 선호

    일반적으로 [] 기반의 색인은 정수가 포함될 경우 이를 레이블로 인식, 색인의 자료형에 따라 작동 방식이 달라짐

    In [144]: obj1 = pd.Series([1, 2, 3], index=[2, 0, 1])
    
    In [145]: obj2 = pd.Series([1, 2, 3], index=['a', 'b', 'c'])
    
    In [146]: obj1
    Out[146]:
    2    1
    0    2
    1    3
    dtype: int64
    
    In [147]: obj2
    Out[147]:
    a    1
    b    2
    c    3
    dtype: int64
    
    In [148]: obj1[[0, 1, 2]]
    Out[148]:
    0    2
    1    3
    2    1
    dtype: int64
    
    In [149]: obj2[[0, 1, 2]]
    Out[149]:
    a    1
    b    2
    c    3
    dtype: int64
    
    In [150]: obj1, obj2
    Out[150]:
    (2    1
     0    2
     1    3
     dtype: int64,
     a    1
     b    2
     c    3
     dtype: int64)
     
     In [151]: obj2
    Out[151]:
    a    1
    b    2
    c    3
    dtype: int64
    
    In [152]: obj2[ [2, 0, 1] ]
    Out[152]:
    c    3
    a    1
    b    2
    dtype: int64

    loc를 이용하면 색인에 정수가 포함되어 있지 않을 경우 obj.loc[[0, 1, 2]] 구문은 실패

    In [153]: obj2.loc[[0, 1, 2]]
    ---------------------------------------------------------------------------
    KeyError                                  Traceback (most recent call last)
    Cell In[153], line 1
    ----> 1 obj2.loc[[0, 1, 2]]
    
    KeyError: "None of [Index([0, 1, 2], dtype='int32')] are in the [index]"

    loc 연산자는 레이블로만 색인을 취함

    색인이 정수를 포함하고 있는지 여부와 관계없이 일관되게 작동하도록 정수로만 색인을 취하는 iloc 연산자도 존재

    In [154]: obj2.loc[['a', 'b', 'c']]
    Out[154]:
    a    1
    b    2
    c    3
    dtype: int64
    
    In [155]: obj1.iloc[[0, 1, 2]]
    Out[155]:
    2    1
    0    2
    1    3
    dtype: int64
    
    In [156]: obj2.iloc[[0, 1, 2]]
    Out[156]:
    a    1
    b    2
    c    3
    dtype: int64

     

    색인으로 DataFrame에서 하나 이사으이 열의 값을 가져올 수 있음

    In [157]: data = pd.DataFrame(np.arange(16).reshape((4, 4)),
         ...:                     index=['Ohio', 'Colorado', 'Utah', 'New York'],
         ...:                     columns=['one', 'two', 'three', 'four'])
    
    In [158]: data
    Out[158]:
              one  two  three  four
    Ohio        0    1      2     3
    Colorado    4    5      6     7
    Utah        8    9     10    11
    New York   12   13     14    15
    
    In [159]: data['two']
    Out[159]:
    Ohio         1
    Colorado     5
    Utah         9
    New York    13
    Name: two, dtype: int32
    
    In [160]: data[['three', 'one']]
    Out[160]:
              three  one
    Ohio          2    0
    Colorado      6    4
    Utah         10    8
    New York     14   12
    
    In [162]: pd.DataFrame(data, columns=['two'])
    Out[162]:
              two
    Ohio        1
    Colorado    5
    Utah        9
    New York   13
    
    # 'Ohio' 가지고 오고 싶은 경우
    
    In [163]: data.loc['Ohio']
    Out[163]:
    one      0
    two      1
    three    2
    four     3
    Name: Ohio, dtype: int32
    
    In [164]: data.iloc[0]
    Out[164]:
    one      0
    two      1
    three    2
    four     3
    Name: Ohio, dtype: int32

     

    슬라이싱으로 행을 선택하거나 불리언 배열로 행을 선택할 수 있음

    In [165]: data[:2]
    Out[165]:
              one  two  three  four
    Ohio        0    1      2     3
    Colorado    4    5      6     7
    
    In [166]: data[data['three'] > 5]
    Out[166]:
              one  two  three  four
    Colorado    4    5      6     7
    Utah        8    9     10    11
    New York   12   13     14    15

     

    또 다른 방법으로는 스칼라 비교를 통해 생성된 불리언 DataFrame을 사용해 값을 선택하는 것

    In [167]: data < 5
    Out[167]:
                one    two  three   four
    Ohio       True   True   True   True
    Colorado   True  False  False  False
    Utah      False  False  False  False
    New York  False  False  False  False
    
    In [168]: data[data < 5] = 0
    
    In [169]: data
    Out[169]:
              one  two  three  four
    Ohio        0    0      0     0
    Colorado    0    5      6     7
    Utah        8    9     10    11
    New York   12   13     14    15

    DataFrame을 사용해 True 값을 가진 위치에 0을 할당할 수 있음

     

    loc과 iloc로 선택

    Series와 마찬가지로 DataFrame에는 레이블과 정수 기반 색인을 위한 loc, iloc 속성이 존재

    DataFrame은 2차원이므로 축 레이블(loc) 또는 정수 색인(iloc)을 이용해 행렬의 부분집합 선택

    In [173]: data
    Out[173]:
              one  two  three  four
    Ohio        0    0      0     0
    Colorado    0    5      6     7
    Utah        8    9     10    11
    New York   12   13     14    15
    
    In [174]: data.loc['Colorado']
    Out[174]:
    one      0
    two      5
    three    6
    four     7
    Name: Colorado, dtype: int32
    
    In [175]: data.loc[['Colorado', 'New York']]
    Out[175]:
              one  two  three  four
    Colorado    0    5      6     7
    New York   12   13     14    15
    
    In [176]: data.loc['Colorado', ['two', 'three']]
    Out[176]:
    two      5
    three    6
    Name: Colorado, dtype: int32

    행과 열 조합해 선택 가능

     

    In [177]: data
    Out[177]:
              one  two  three  four
    Ohio        0    0      0     0
    Colorado    0    5      6     7
    Utah        8    9     10    11
    New York   12   13     14    15
    
    In [178]: data.iloc[2]
    Out[178]:
    one       8
    two       9
    three    10
    four     11
    Name: Utah, dtype: int32
    
    In [179]: data.iloc[[2, 1]]
    Out[179]:
              one  two  three  four
    Utah        8    9     10    11
    Colorado    0    5      6     7
    
    In [180]: data.iloc[2, [3, 0, 1]]
    Out[180]:
    four    11
    one      8
    two      9
    Name: Utah, dtype: int32
    
    In [181]: data.iloc[[1, 2], [3, 0, 1]]
    Out[181]:
              four  one  two
    Colorado     7    0    5
    Utah        11    8    9

     

    두 색인 함수는 슬라이스도 지원, 단일 레이블이나 레이블 리스트도 지원함

    In [182]: data.loc[:'Utah', 'two']
    Out[182]:
    Ohio        0
    Colorado    5
    Utah        9
    Name: two, dtype: int32
    
    In [183]: data.iloc[:, :3][data.three > 5]
    Out[183]:
              one  two  three
    Colorado    0    5      6
    Utah        8    9     10
    New York   12   13     14

     

    loc에는 불리언 배열을 사용할 수 있지만 iloc에서는 사용할 수 없음

    In [184]: data.loc[data.three >= 2]
    Out[184]:
              one  two  three  four
    Colorado    0    5      6     7
    Utah        8    9     10    11
    New York   12   13     14    15

     

    정수 색인의 함정

    정수 색인으로 판다스 객체를 다루다 보면 리스트나 튜플 같은 파이썬 내장 자료구조에서 색인을 다루는 방법과의 차이점 때문에 실수하는 경우 발생

    In [185]: ser = pd.Series(np.arange(3.))
    
    In [186]: ser
    Out[186]:
    0    0.0
    1    1.0
    2    2.0
    dtype: float64
    
    In [188]: ser[1:2]
    Out[188]:
    1    1.0
    dtype: float64
    
    In [189]: ser[1]
    Out[189]: 1.0
    
    In [187]: ser[-1]
    ---------------------------------------------------------------------------
    ValueError                                Traceback (most recent call last)
    File ~\anaconda3\Lib\site-packages\pandas\core\indexes\range.py:345, in RangeIndex.get_loc(self, key)
        344 try:
    --> 345     return self._range.index(new_key)
        346 except ValueError as err:
    
    ValueError: -1 is not in range
    
    The above exception was the direct cause of the following exception:
    
    KeyError                                  Traceback (most recent call last)
    Cell In[187], line 1
    ----> 1 ser[-1]
    
    File ~\anaconda3\Lib\site-packages\pandas\core\series.py:1007, in Series.__getitem__(self, key)
       1004     return self._values[key]
       1006 elif key_is_scalar:
    -> 1007     return self._get_value(key)
       1009 if is_hashable(key):
       1010     # Otherwise index.get_value will raise InvalidIndexError
       1011     try:
       1012         # For labels that don't resolve as scalars like tuples and frozensets
    
    File ~\anaconda3\Lib\site-packages\pandas\core\series.py:1116, in Series._get_value(self, label, takeable)
       1113     return self._values[label]
       1115 # Similar to Index.get_value, but we do not fall back to positional
    -> 1116 loc = self.index.get_loc(label)
       1118 if is_integer(loc):
       1119     return self._values[loc]
    
    File ~\anaconda3\Lib\site-packages\pandas\core\indexes\range.py:347, in RangeIndex.get_loc(self, key)
        345         return self._range.index(new_key)
        346     except ValueError as err:
    --> 347         raise KeyError(key) from err
        348 if isinstance(key, Hashable):
        349     raise KeyError(key)
    
    KeyError: -1

    판다스는 레이블 색인을 찾는 데 실패해 정수 색인으로 값을 찾음

    레이블 색인이 0, 1, 2를 포함하는 경우 파이썬은 사용자가 레이블 색인으로 선택하고 싶은 건지, 정수 색인으로 선택하고 싶은 건지 추측하기 쉽지 않음

    In [190]: ser
    Out[190]:
    0    0.0
    1    1.0
    2    2.0
    dtype: float64
    
    In [191]: ser2 = pd.Series(np.arange(3.), index=['a', 'b', 'c'])
    
    In [192]: ser2[-1]
    Out[192]: 2.0
    
    In [193]: ser2.iloc[-1]
    Out[193]: 2.0

     

    레이블에 대해 loc, 정수 색인에 대해 iloc를 사용하면 원하던 값을 얻을 수 있음

    항상 loc, iloc 사용해 모호함 피할 것

     

    산술 연산과 데이터 정렬

    서로 다른 인덱스를 가지고 있는 객체 간의 산술 연산 간단하게 처리 가능

    객체를 더할 때 짝이 맞지 않는 인덱스가 있다면 결과에 두 색인이 통합됨

    In [46]: s1 = pd.Series([7.3, -2.5, 3.4, 1.5], index=['a', 'c', 'd', 'e'])
    
    In [47]: s2 = pd.Series([-2.1, 3.6, -1.5, 4, 3.1],
        ...:                index=['a', 'c', 'e', 'f', 'g'])
    
    In [48]: s1
    Out[48]:
    a    7.3
    c   -2.5
    d    3.4
    e    1.5
    dtype: float64
    
    In [49]: s2
    Out[49]:
    a   -2.1
    c    3.6
    e   -1.5
    f    4.0
    g    3.1
    dtype: float64
    
    In [50]: s1 + s2
    Out[50]:
    a    5.2
    c    1.1
    d    NaN
    e    0.0
    f    NaN
    g    NaN
    dtype: float64
    
    # 서로 겹치는 인덱스가 없는 경우 데이터는 결측치가 됨
    # 추후 산술 연산 시 누락된 값 전달되며 DataFrame의 경우 행과 열 모두에 정렬 적용
    In [51]: df1 = pd.DataFrame(np.arange(9.).reshape((3, 3)), columns=list('bcd'),
        ...:                    index=['Ohio', 'Texas', 'Colorado'])
    
    In [52]: df2 = pd.DataFrame(np.arange(12.).reshape((4, 3)), columns=list('bde'),
        ...:                    index=['Utah', 'Ohio', 'Texas', 'Oregon'])
    
    In [53]: df1
    Out[53]:
                b    c    d
    Ohio      0.0  1.0  2.0
    Texas     3.0  4.0  5.0
    Colorado  6.0  7.0  8.0
    
    In [54]: df2
    Out[54]:
              b     d     e
    Utah    0.0   1.0   2.0
    Ohio    3.0   4.0   5.0
    Texas   6.0   7.0   8.0
    Oregon  9.0  10.0  11.0
    
    In [55]: df1 + df2
    Out[55]:
                b   c     d   e
    Colorado  NaN NaN   NaN NaN
    Ohio      3.0 NaN   6.0 NaN
    Oregon    NaN NaN   NaN NaN
    Texas     9.0 NaN  12.0 NaN
    Utah      NaN NaN   NaN NaN

     

    공통 열이나 행 레이블이 없는 DataFrame을 더하면 결과는 아무것도 나타나지 않음

    In [58]: df1
    Out[58]:
       A
    0  1
    1  2
    
    In [59]: df2
    Out[59]:
       B
    0  3
    1  4
    
    In [60]: df1 + df2
    Out[60]:
        A   B
    0 NaN NaN
    1 NaN NaN

     

    산술 연산 메서드에 채워 넣을 값 지정

    색인이 서로 다른 객체 간의 산술 연산에서 존재하지 않는 축의 값을 0같이 특수한 값으로 지정하고 싶은 경우

    np.nan을 대입해 NA로 설정

    In [62]: df1 = pd.DataFrame(np.arange(12.).reshape((3, 4)),
        ...:                    columns=list('abcd'))
    
    In [63]: df2 = pd.DataFrame(np.arange(20.).reshape((4, 5)),
        ...:                    columns=list('abcde'))
    
    In [64]: df2.loc[1, 'b'] = np.nan
    
    In [65]: df1
    Out[65]:
         a    b     c     d
    0  0.0  1.0   2.0   3.0
    1  4.0  5.0   6.0   7.0
    2  8.0  9.0  10.0  11.0
    
    In [66]: df2
    Out[66]:
          a     b     c     d     e
    0   0.0   1.0   2.0   3.0   4.0
    1   5.0   NaN   7.0   8.0   9.0
    2  10.0  11.0  12.0  13.0  14.0
    3  15.0  16.0  17.0  18.0  19.0
    
    In [67]: df1 + df2
    Out[67]:
          a     b     c     d   e
    0   0.0   2.0   4.0   6.0 NaN
    1   9.0   NaN  13.0  15.0 NaN
    2  18.0  20.0  22.0  24.0 NaN
    3   NaN   NaN   NaN   NaN NaN

    df1에 add 메서드를 사용해 df2와 fill_value 값을 인수로 전달

    연산에서 누락된 값은 fill_value 값으로 대체

    In [68]: df1.add(df2, fill_value=0)
    Out[68]:
          a     b     c     d     e
    0   0.0   2.0   4.0   6.0   4.0
    1   9.0   5.0  13.0  15.0   9.0
    2  18.0  20.0  22.0  24.0  14.0
    3  15.0  16.0  17.0  18.0  19.0

     

    각각의 산술 연산 메서드는 r로 시작하는 계산 인수를 뒤집어 계산하는 짝꿍 메서드를 가짐

    두 코드의 계산 결과는 같음

    In [69]: 1 / df1
    Out[69]:
           a         b         c         d
    0    inf  1.000000  0.500000  0.333333
    1  0.250  0.200000  0.166667  0.142857
    2  0.125  0.111111  0.100000  0.090909
    
    In [70]: df1.rdiv(1)
    Out[70]:
           a         b         c         d
    0    inf  1.000000  0.500000  0.333333
    1  0.250  0.200000  0.166667  0.142857
    2  0.125  0.111111  0.100000  0.090909

     

    Series나 DataFrame을 재색인할 때도 fill_value 지정 가능

    In [71]: df1
    Out[71]:
         a    b     c     d
    0  0.0  1.0   2.0   3.0
    1  4.0  5.0   6.0   7.0
    2  8.0  9.0  10.0  11.0
    
    In [72]: df2
    Out[72]:
          a     b     c     d     e
    0   0.0   1.0   2.0   3.0   4.0
    1   5.0   NaN   7.0   8.0   9.0
    2  10.0  11.0  12.0  13.0  14.0
    3  15.0  16.0  17.0  18.0  19.0
    
    In [73]: df1.reindex(columns=df2.columns, fill_value=0)
    Out[73]:
         a    b     c     d  e
    0  0.0  1.0   2.0   3.0  0
    1  4.0  5.0   6.0   7.0  0
    2  8.0  9.0  10.0  11.0  0
    
    In [74]: df1.reindex(columns=df2.columns)
    Out[74]:
         a    b     c     d   e
    0  0.0  1.0   2.0   3.0 NaN
    1  4.0  5.0   6.0   7.0 NaN
    2  8.0  9.0  10.0  11.0 NaN

     

    DataFrame과 Series 간의 연산

    In [75]: arr = np.arange(12.).reshape((3, 4))
    
    In [76]: arr
    Out[76]:
    array([[ 0.,  1.,  2.,  3.],
           [ 4.,  5.,  6.,  7.],
           [ 8.,  9., 10., 11.]])
    
    In [77]: arr[0]
    Out[77]: array([0., 1., 2., 3.])
    
    In [78]: arr - arr[0]
    Out[78]:
    array([[0., 0., 0., 0.],
           [4., 4., 4., 4.],
           [8., 8., 8., 8.]])
           
           
    In [79]: arr1 = np.arange(12.).reshape((3, 4))
    
    In [80]: arr1
    Out[80]:
    array([[ 0.,  1.,  2.,  3.],
           [ 4.,  5.,  6.,  7.],
           [ 8.,  9., 10., 11.]])
    
    In [81]: arr2 = np.array( [1, 2, 3, 4] )
    
    In [82]: arr2
    Out[82]: array([1, 2, 3, 4])
    
    In [83]: arr1- arr2
    Out[83]:
    array([[-1., -1., -1., -1.],
           [ 3.,  3.,  3.,  3.],
           [ 7.,  7.,  7.,  7.]])
    
    In [84]: arr = np.arange(12.).reshape((3, 4))
    
    In [85]: arr
    Out[85]:
    array([[ 0.,  1.,  2.,  3.],
           [ 4.,  5.,  6.,  7.],
           [ 8.,  9., 10., 11.]])
    
    In [86]: arr[0]
    Out[86]: array([0., 1., 2., 3.])
    
    In [87]: arr - arr[0]
    Out[87]:
    array([[0., 0., 0., 0.],
           [4., 4., 4., 4.],
           [8., 8., 8., 8.]])
    
    In [88]: arr[0][:]
    Out[88]: array([0., 1., 2., 3.])

    arr에서 arr[0]을 빼면 계산은 각 행에 대해 한 번씩만 수행되며 브로드캐스팅(broadcasting)이라 부름

    DataFrame과 Series 간의 연산은 이와 유사

    In [90]: frame = pd.DataFrame(np.arange(12.).reshape((4, 3)),
        ...:                      columns=list('bde'),
        ...:                      index=['Utah', 'Ohio', 'Texas', 'Oregon'])
    
    In [91]: series = frame.iloc[0]
    
    In [92]: frame
    Out[92]:
              b     d     e
    Utah    0.0   1.0   2.0
    Ohio    3.0   4.0   5.0
    Texas   6.0   7.0   8.0
    Oregon  9.0  10.0  11.0
    
    In [93]: series
    Out[93]:
    b    0.0
    d    1.0
    e    2.0
    Name: Utah, dtype: float64

     

    기본적으로 DataFrame과 Series 간의 산술 연산은 Series의 색인을 DataFrame의 열에 맞추고 아래 행으로 브로드캐스팅

    In [94]: frame - series
    Out[94]:
              b    d    e
    Utah    0.0  0.0  0.0
    Ohio    3.0  3.0  3.0
    Texas   6.0  6.0  6.0
    Oregon  9.0  9.0  9.0

     

    만약 색인값을 DataFrame의 열이나 Series의 색인에서 찾을 수 없다면 그 객체는 형식을 맞추기 위해 재색인됨

    In [95]: series2 = pd.Series(np.arange(3), index=['b', 'e', 'f'])
    
    In [96]: series2
    Out[96]:
    b    0
    e    1
    f    2
    dtype: int32
    
    In [97]: frame + series2
    Out[97]:
              b   d     e   f
    Utah    0.0 NaN   3.0 NaN
    Ohio    3.0 NaN   6.0 NaN
    Texas   6.0 NaN   9.0 NaN
    Oregon  9.0 NaN  12.0 NaN

    브로드캐스팅 대신 각 행에 대해 연산을 수행하고 싶다면 산술 연산 메서드를 사용해 색인이 일치하도록 지정

    In [98]: series3 = frame['d']
    
    In [99]: frame
    Out[99]:
              b     d     e
    Utah    0.0   1.0   2.0
    Ohio    3.0   4.0   5.0
    Texas   6.0   7.0   8.0
    Oregon  9.0  10.0  11.0
    
    In [100]: series3
    Out[100]:
    Utah       1.0
    Ohio       4.0
    Texas      7.0
    Oregon    10.0
    Name: d, dtype: float64
    
    In [101]: frame.sub(series3, axis='index')
    Out[101]:
              b    d    e
    Utah   -1.0  0.0  1.0
    Ohio   -1.0  0.0  1.0
    Texas  -1.0  0.0  1.0
    Oregon -1.0  0.0  1.0

    인수로 넘기는 axis 값은 연산을 적용할 축 번호, 예제 코드에서 axis='index'는 DataFrame의 열을 따라 연산을 수행하라는 의미

     

    함수 적용과 매핑

    판다스 객체에도 넘파이의 유니버설 함수(배열의 각 원소에 적용되는 메서드)를 적용할 수 있음

    In [102]: frame = pd.DataFrame(np.random.standard_normal((4, 3)),
         ...:                      columns=list('bde'),
         ...:                      index=['Utah', 'Ohio', 'Texas', 'Oregon'])
    
    In [103]: frame
    Out[103]:
                   b         d         e
    Utah    0.570715 -1.009360 -0.759693
    Ohio   -0.716131 -0.455551  1.013025
    Texas  -0.482878 -1.846770 -0.492987
    Oregon  1.276539  0.185498 -0.273742
    
    In [104]: np.abs(frame)
    Out[104]:
                   b         d         e
    Utah    0.570715  1.009360  0.759693
    Ohio    0.716131  0.455551  1.013025
    Texas   0.482878  1.846770  0.492987
    Oregon  1.276539  0.185498  0.273742

    절대값 구하는 abs 적용

     

    자주 사용하는 연산은 각 행이나 열의 1차원 배열에 함수 적용하는 것

    DataFrame의 apply 메서드

    In [106]: def f1(x):
         ...:     return x.max() - x.min()
         ...:
    
    In [107]: frame.apply(f1)
    Out[107]:
    b    1.992670
    d    2.032268
    e    1.772718
    dtype: float64
    
    In [108]: frame.apply(f1, axis='columns')
    Out[108]:
    Utah      1.580075
    Ohio      1.729156
    Texas     1.363892
    Oregon    1.550281
    dtype: float64

    apply 함수에서 axis='columns' 인수를 넘기면 각 행에 대해 한 번씩만 수행됨, '열을 따라 적용된다'라고 이해할 것

     

    합계(sum)나 평균(mean)은 DataFrame의 메서드로 존재하므로 apply 메서드를 사용해야만 하는 것은 아님

    apply 메서드에 전달된 함수는 스칼라 값을 반환할 필요 없음

    > 여러 값을 가진 Series를 반환해도 됨

    In [109]: def f2(x):
         ...:     return pd.Series([x.min(), x.max()], index=['min', 'max'])
         ...:
    
    In [110]: frame.apply(f2)
    Out[110]:
                b         d         e
    min -0.716131 -1.846770 -0.759693
    max  1.276539  0.185498  1.013025

    배열의 각 원소에 적용되는 파이썬 함수를 사용할 수 있음

    frame 객체에서 부동소수점을 문자열 포맷으로 변환하고 싶은 경우 applymap을 이용해 수행 가능

    In [111]: def my_format(x):
         ...:     return f"{x:.2f}"
         ...:
    
    In [112]: frame.applymap(my_format)
    Out[112]:
                b      d      e
    Utah     0.57  -1.01  -0.76
    Ohio    -0.72  -0.46   1.01
    Texas   -0.48  -1.85  -0.49
    Oregon   1.28   0.19  -0.27
    
    In [113]: frame['e']
    Out[113]:
    Utah     -0.759693
    Ohio      1.013025
    Texas    -0.492987
    Oregon   -0.273742
    Name: e, dtype: float64
    
    In [114]: frame['e'].map(my_format)
    Out[114]:
    Utah      -0.76
    Ohio       1.01
    Texas     -0.49
    Oregon    -0.27
    Name: e, dtype: object

    메서드 이름이 applymap인 이유는 Series가 각 원소에 적용할 함수를 지정하기 위한 map 메서드를 갖기 때문

     

    정렬과 순위

    정렬된 새로운 객체를 반환하는 sort_index 메서드를 사용해 행과 열의 인덱스를 알파벳순으로 정렬할 수 있음

    In [115]: obj = pd.Series(np.arange(4), index=['d', 'a', 'b', 'c'])
    
    In [116]: obj
    Out[116]:
    d    0
    a    1
    b    2
    c    3
    dtype: int32
    
    In [117]: obj.sort_index()
    Out[117]:
    a    1
    b    2
    c    3
    d    0
    dtype: int32

     

    DataFrame은 행과 열 중 하나의 인덱스를 기준으로 정렬할 수 있음

    In [118]: frame = pd.DataFrame(np.arange(8).reshape((2, 4)),
         ...:                      index=['three', 'one'],
         ...:                      columns=['d', 'a', 'b', 'c'])
    
    In [119]: frame
    Out[119]:
           d  a  b  c
    three  0  1  2  3
    one    4  5  6  7
    
    In [120]: frame.sort_index()
    Out[120]:
           d  a  b  c
    one    4  5  6  7
    three  0  1  2  3
    
    In [121]: frame.sort_index(axis='columns')
    Out[121]:
           a  b  c  d
    three  1  2  3  0
    one    5  6  7  4

    데이터는 기본적으로 오름차순 정렬, 내림차순으로도 정렬할 수 있음

    In [122]: frame.sort_index(axis='columns', ascending=False)
    Out[122]:
           d  c  b  a
    three  0  3  2  1
    one    4  7  6  5

     

    Series 객체를 값에 따라 정렬: sort_values

    In [123]: obj = pd.Series([4, 7, -3, 2])
    
    In [124]: obj.sort_values()
    Out[124]:
    2   -3
    3    2
    0    4
    1    7
    dtype: int64
    
    # 정렬할 때 누락된 값(결측치)은 기본적으로 Series 객체의 가장 마지막에 위치
    In [125]: obj = pd.Series([4, np.nan, 7, np.nan, -3, 2])
    
    In [126]: obj.sort_values()
    Out[126]:
    4   -3.0
    5    2.0
    0    4.0
    2    7.0
    1    NaN
    3    NaN
    dtype: float64
    
    # + asc False 적용 가능
    In [128]: obj = pd.Series([4, 7, -3, 2])
    
    In [129]: obj.sort_values()
    Out[129]:
    2   -3
    3    2
    0    4
    1    7
    dtype: int64
    
    In [130]: obj.sort_values(ascending=False)
    Out[130]:
    1    7
    0    4
    3    2
    2   -3
    dtype: int64

     

     

    # na_position 옵션을 사용해 누락된 값을 먼저 정렬할 수 있음
    In [131]: obj = pd.Series([4, np.nan, 7, np.nan, -3, 2])
    
    In [132]: obj.sort_values(na_position='first')
    Out[132]:
    1    NaN
    3    NaN
    4   -3.0
    5    2.0
    0    4.0
    2    7.0
    dtype: float64
    
    # DataFrame에서 하나 이상의 열에 있는 값으로 정렬하는 경우 필요한 열의 이름을 넘기기
    In [133]: frame = pd.DataFrame({'b': [4, 7, -3, 2], 'a': [0, 1, 0, 1]})
    
    In [134]: frame
    Out[134]:
       b  a
    0  4  0
    1  7  1
    2 -3  0
    3  2  1
    
    In [135]: frame.sort_values('b')
    Out[135]:
       b  a
    2 -3  0
    3  2  1
    0  4  0
    1  7  1
    
    # 여러 개의 열을 정렬할 때는 열 이름 리스트 넘기기
    In [136]: frame.sort_values(['a', 'b'])
    Out[136]:
       b  a
    2 -3  0
    0  4  0
    3  2  1
    1  7  1

    리스트 아닌 튜플에서는 에러

     

    순위는 가장 낮은 값부터 시작해 배열의 유효한 데이터 개수까지의 순서를 매김

    기본적으로 Series와 DataFrame의 rank 메서드는 동점인 항목에 대해서는 평균 순위를 매겨 책정

    In [137]: obj = pd.Series([7, -5, 7, 4, 2, 0, 4])
    
    In [138]: obj.rank()
    Out[138]:
    0    6.5
    1    1.0
    2    6.5
    3    4.5
    4    3.0
    5    2.0
    6    4.5
    dtype: float64
    
    # 데이터상에서 나타나는 순서에 따라 순위를 매길 수 있음
    In [139]: obj.rank(method='first')
    Out[139]:
    0    6.0
    1    1.0
    2    7.0
    3    4.0
    4    3.0
    5    2.0
    6    5.0
    dtype: float64
    # 0번째, 2번째 인덱스에 대해 평균 순위인 6.5를 적용하는 대신 먼저 출현한 순서대로
    # 6과 7 적용
    
    # 내림차순
    In [140]: obj.rank(ascending=False)
    Out[140]:
    0    1.5
    1    7.0
    2    1.5
    3    3.5
    4    5.0
    5    6.0
    6    3.5
    dtype: float64

     

    DataFrame에서는 행과 열에 대해 순위를 정할 수 있음

    In [141]: frame = pd.DataFrame({'b': [4.3, 7, -3, 2], 'a': [0, 1, 0, 1],
         ...:                       'c': [-2, 5, 8, -2.5]})
    
    In [142]: frame
    Out[142]:
         b  a    c
    0  4.3  0 -2.0
    1  7.0  1  5.0
    2 -3.0  0  8.0
    3  2.0  1 -2.5
    
    In [143]: frame.rank(axis='columns')
    Out[143]:
         b    a    c
    0  3.0  2.0  1.0
    1  3.0  1.0  2.0
    2  1.0  2.0  3.0
    3  3.0  2.0  1.0

     

    중복 색인

    판다스의 많은 함수(reindex 같은)에서 색인값은 유일해야 하지만 의무는 아님

    중복된 색인값을 갖는 Series 객체

    In [153]: obj = pd.Series(np.arange(5), index=['a', 'a', 'b', 'b', 'c'])
    
    In [154]: obj
    Out[154]:
    a    0
    a    1
    b    2
    b    3
    c    4
    dtype: int32
    
    In [155]: # is_unique 속성은 해당 값이 유일한지 아닌지 알려 줌
    
    In [156]: obj.index.is_unique
    Out[156]: False
    
    # 유일한 색인만 있다면 스칼라 값 반환, 중복되는 색인값 있을 때는 하나의 Series 객체 반환
    In [157]: obj['a']
    Out[157]:
    a    0
    a    1
    dtype: int32
    
    In [158]: obj['c']
    Out[158]: 4
    
    # 레이블의 반복 여부에 따라 색인을 이용해 선택한 결과가 다를 수 있기 때문에
    # 코드가 복잡해질 수 있음
    
    In [159]: df = pd.DataFrame(np.random.standard_normal((5, 3)),
         ...:                   index=['a', 'a', 'b', 'b', 'c'])
    
    In [160]: df
    Out[160]:
              0         1         2
    a  0.391695 -0.442054 -0.033109
    a  0.219205 -0.241442 -0.032905
    b  1.263946  1.230159  1.335586
    b -0.775331 -0.474710  0.230852
    c -0.503077  0.062859  0.881807
    
    In [161]: df.loc['b']
    Out[161]:
              0         1         2
    b  1.263946  1.230159  1.335586
    b -0.775331 -0.474710  0.230852
    
    In [162]: df.loc['c']
    Out[162]:
    0   -0.503077
    1    0.062859
    2    0.881807
    Name: c, dtype: float64

     

    기술 통계 계산과 요약

    판다스 객체는 일반적인 수학 메서드와 통계 메서드를 갖고 있음

    메서드 대부분은 하나의 Series나 DataFrame의 행과 열에서 단일 값(합, 평균 같은)을 구하는 축소(reduction) 혹은 요약 통계(summary statistics) 범주에 속함

    순수 넘파이 배열에서 제공하는 동일 메서드와는 다르게 판다스 메서드는 처음부터 누락된 데이터를 제외하도록 설계

    In [163]: df = pd.DataFrame([[1.4, np.nan], [7.1, -4.5],
         ...:                   [np.nan, np.nan], [0.75, -1.3]],
         ...:                   index=['a', 'b', 'c', 'd'],
         ...:                   columns=['one', 'two'])
    
    In [164]: df
    Out[164]:
        one  two
    a  1.40  NaN
    b  7.10 -4.5
    c   NaN  NaN
    d  0.75 -1.3
    In [165]: df.sum()
    Out[165]:
    one    9.25
    two   -5.80
    dtype: float64
    
    # axis='columns' 혹은 axis=1 옵션을 넘기면 각 열의 합 반환
    In [166]: df.sum(axis='columns')
    Out[166]:
    a    1.40
    b    2.60
    c    0.00
    d   -0.55
    dtype: float64

     

    전체 행과 열의 모든 값이 NA 값이라면 그 합은 0이 되고 값이 하나라도 NA라면 결괏값은 NA가 됨

    > skipna 옵션으로 비활성화할 수 있으며 이 경우 행과 열의 NA 값은 해당 결과를 NA로 지정

    In [169]: df.sum(axis='index', skipna=False)    # 결측치 포함
    Out[169]:
    one   NaN
    two   NaN
    dtype: float64
    
    In [170]: df.sum(axis='columns', skipna=False)    # 결측치 포함
    Out[170]:
    a     NaN
    b    2.60
    c     NaN
    d   -0.55
    dtype: float64

     

    평균 같은 일부 집계에는 결괏값을 생성하기 위해 최소 하나 이상의 NA가 아닌 값 필요

    In [171]: df.mean(axis='columns')
    Out[171]:
    a    1.400
    b    1.300
    c      NaN
    d   -0.275
    dtype: float64
    In [172]: df.mean(axis='index')
    Out[172]:
    one    3.083333
    two   -2.900000
    dtype: float64

     

    idxmin이나 idxmax 같은 메서드는 최소 혹은 최댓값을 가진 색인값 같은 간접 통계(indirect statistics) 반환

    In [173]: df
    Out[173]:
        one  two
    a  1.40  NaN
    b  7.10 -4.5
    c   NaN  NaN
    d  0.75 -1.3
    
    In [174]: df.idxmin()
    Out[174]:
    one    d
    two    b
    dtype: object
    
    In [175]: df.idxmax()
    Out[175]:
    one    b
    two    d
    dtype: object
    
    # 누산(accumulation)
    In [176]: df.cumsum()
    Out[176]:
        one  two
    a  1.40  NaN
    b  8.50 -4.5
    c   NaN  NaN
    d  9.25 -5.8
    
    # 한 번에 여러 개의 요약 통계를 만드는 describe 메서드
    
    In [177]: df.describe()
    Out[177]:
                one       two
    count  3.000000  2.000000
    mean   3.083333 -2.900000
    std    3.493685  2.262742
    min    0.750000 -4.500000
    25%    1.075000 -3.700000
    50%    1.400000 -2.900000
    75%    4.250000 -2.100000
    max    7.100000 -1.300000
    
    # 수치 데이터가 아닐 경우 describe는 다른 요약 통계 생성
    In [178]: obj = pd.Series(['a', 'a', 'b', 'c'] * 4)
    
    In [179]: obj.describe()
    Out[179]:
    count     16
    unique     3
    top        a
    freq       8
    dtype: object

     

    유일값, 값 세기, 멤버십

    In [11]: obj = pd.Series(['c', 'a', 'd', 'a', 'a', 'b', 'b', 'c', 'c'])
    
    In [12]: uniques = obj.unique()
    
    In [13]: uniques
    Out[13]: array(['c', 'a', 'd', 'b'], dtype=object)

    unique 함수는 Series에서 중복되는 값 제거하고 유일한 값(unique value)만 담은 Series 반환

    정렬된 상태로 반환되지 않음, uniques.sort()를 이용해 나중에 정렬 가능

    sort 메서드는 원본 변경함

    In [14]: obj.value_counts()    # 도수(데이터의 개수) 반환
    Out[14]:
    c    3
    a    3
    b    2
    d    1
    Name: count, dtype: int64
    
    In [15]: pd.value_counts(obj.to_numpy(), sort=False)    # 위와 같은 코드
    Out[15]:
    c    3
    a    3
    d    1
    b    2
    Name: count, dtype: int64
    
    In [16]: pd.value_counts(obj.to_numpy(), sort=True)
    Out[16]:
    c    3
    a    3
    b    2
    d    1
    Name: count, dtype: int64

     

    isin 메서드는 어떤 값이 Series에 존재하는지 나타내는 불리언 벡터 반환

    Series나 DataFrame의 열에서 값을 골라내고 싶을 때 사용

    In [17]: obj    # obj는 Pandas로 만들어진 Series 객체
    Out[17]:
    0    c
    1    a
    2    d
    3    a
    4    a
    5    b
    6    b
    7    c
    8    c
    dtype: object
    
    In [18]: mask = obj.isin(['b', 'c'])
    
    In [19]: mask
    Out[19]:
    0     True
    1    False
    2    False
    3    False
    4    False
    5     True
    6     True
    7     True
    8     True
    dtype: bool
    
    In [20]: obj[mask]
    Out[20]:
    0    c
    5    b
    6    b
    7    c
    8    c
    dtype: object

     

    isin과 관련 있는 Index_get.indexer 메서드는 여러 값이 들어 있는 배열에서 유일한 값의 인덱스 배열을 구할 수 있음

    In [23]: to_match = pd.Series(['c', 'a', 'b', 'b', 'c', 'a'])
    
    In [24]: unique_vals = pd.Series(['c', 'b', 'a'])
    
    In [25]: indices = pd.Index(unique_vals).get_indexer(to_match)
    
    In [26]: indices
    Out[26]: array([0, 2, 1, 1, 0, 2], dtype=int64)

     

    메서드 설명
    isin Series의 각 원소가 넘겨받은 연속된 값에 속하는지 나타내는 불리언 배열 반환
    get_indexer 각 값에 대해 유일한 값을 담고 있는 배열에서의 정수 인덱스 계산, 데이터 정렬이나 조인 형태의 연산을 하는 경우
    unique Series에서 중복되는 값을 제거하고 유일한 값만 포함하는 배열 반환, 결과는 Series에서 발견된 순서대로 반환
    value_counts Series에서 유일한 값에 대한 인덱스와 도수 계산, 도수는 내림차순으로 정렬됨

     

    DataFrame의 여러 열에 대해 히스토그램(histogram) 계산

    In [27]: data = pd.DataFrame({'Qu1': [1, 3, 4, 3, 4],
        ...:                      'Qu2': [2, 3, 1, 2, 3],
        ...:                      'Qu3': [1, 5, 2, 4, 4]})
    
    In [28]: data
    Out[28]:
       Qu1  Qu2  Qu3
    0    1    2    1
    1    3    3    5
    2    4    1    2
    3    3    2    4
    4    4    3    4
    
    In [29]: data['Qu1'].value_counts()    # 단일 열 계산
    Out[29]:
    Qu1
    3    2
    4    2
    1    1
    Name: count, dtype: int64
    
    In [30]: data['Qu1'].value_counts().sort_index()
    Out[30]:
    Qu1
    1    1
    3    2
    4    2
    Name: count, dtype: int64

     

    모든 열에서 위와 같은 계산 하려면 DataFrame의 apply 메서드에 pandas.value_counts를 넘기면 됨

    In [31]: result = data.apply(pd.value_counts).fillna(0)    # 위의 계산 모든 열 적용
    
    In [32]: result
    Out[32]:
       Qu1  Qu2  Qu3
    1  1.0  1.0  1.0
    2  0.0  2.0  1.0
    3  2.0  2.0  0.0
    4  2.0  0.0  2.0
    5  0.0  0.0  1.0
    
    # + 계산할 때 NaN이 생기면 에러 생길 수 있으므로 0으로 채워 주기
    # fillna(0) 유무 비교
    In [35]: data.apply(pd.value_counts)
    Out[35]:
       Qu1  Qu2  Qu3
    1  1.0  1.0  1.0
    2  NaN  2.0  1.0
    3  2.0  2.0  NaN
    4  2.0  NaN  2.0
    5  NaN  NaN  1.0
    
    In [36]: data.apply(pd.value_counts).fillna(0)
    Out[36]:
       Qu1  Qu2  Qu3
    1  1.0  1.0  1.0
    2  0.0  2.0  1.0
    3  2.0  2.0  0.0
    4  2.0  0.0  2.0
    5  0.0  0.0  1.0

    전체 열의 유일한 값이 결괏값의 행 레이블, 각 값은 빈도를 나타냄

    DataFrame.value_counts 메서드도 있지만 DataFrame의 각 행을 튜플로 간주해 구별되는 행의 수 계산

    In [37]: data = pd.DataFrame({'a': [1, 1, 1, 2, 2], 'b': [0, 0, 1, 0, 0]})
    
    In [38]: data
    Out[38]:
       a  b
    0  1  0
    1  1  0
    2  1  1
    3  2  0
    4  2  0
    
    In [39]: data.value_counts()
    Out[39]:
    a  b
    1  0    2
    2  0    2
    1  1    1
    Name: count, dtype: int64

    계층적 인덱스로 고유한 행 표시

     

     

Designed by Tistory.