Py) Pandas - 2버전 출시!!

Py) Pandas - 2버전 출시!!

Tabular 데이터핸들링 라이브러리인 Pandas의 2버전이 출시되었다. 이와 관련해여 여러 변경 사항을 알아보자.


개요

드디어 Pandas 2 버전이 출시되었다. 1 버전이 2020년 1월 19일 출시된 이후 약 3년만이다. Pandas 깃헙 저장소에는 엄청난 이슈와 풀리퀘(pull requests)가 등록되어있는데 사람들이 활발하게 기여하는 모습을 보면 오히려 늦은 것 같기도 하다.
Pandas Github 저장소

아무튼 Pandas 공식 사이트에서 제공하는 2버전에서 새로워진 부분을 언급한 웹페이지를 참고하여 작성하였으니 원문을 확인하고자 하는 사람은 해당 페이지를 방문해보면 되겠다.

설치

현재 포스팅 시점으로 최신 버전은 “2.0.0rc1” 이다. 그래서 다음과 같이 주피터 노트북에서 “!pip” 명령어로 설치하면 잘 설치가 되는 것을 볼 수 있다.
※ 아래 실행 결과는 Pandas 2.0.0rc0 버전이 설치 되어있던 상황에서 2.0.0rc1 버전을 설치한 결과이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
!pip install pandas==2.0.0rc1

####
Collecting pandas==2.0.0rc1
Downloading pandas-2.0.0rc1-cp39-cp39-win_amd64.whl (11.3 MB)
---------------------------------------- 11.3/11.3 MB 9.1 MB/s eta 0:00:00
Requirement already satisfied: pytz>=2020.1 in c:\users\encai\anaconda3\lib\site-packages (from pandas==2.0.0rc1) (2022.1)
Requirement already satisfied: numpy>=1.20.3 in c:\users\encai\anaconda3\lib\site-packages (from pandas==2.0.0rc1) (1.21.5)
Requirement already satisfied: python-dateutil>=2.8.2 in c:\users\encai\anaconda3\lib\site-packages (from pandas==2.0.0rc1) (2.8.2)
Requirement already satisfied: six>=1.5 in c:\users\encai\anaconda3\lib\site-packages (from python-dateutil>=2.8.2->pandas==2.0.0rc1) (1.16.0)
Installing collected packages: pandas
Attempting uninstall: pandas
Found existing installation: pandas 2.0.0rc0
Uninstalling pandas-2.0.0rc0:
Successfully uninstalled pandas-2.0.0rc0
Successfully installed pandas-2.0.0rc1

변경점

이전 Pandas 버전(1.5.3) 대비 변경점은 다음과 같다.

개선(Enhancements)

이제 Pandas를 설치할 때 extras를 지정하여 종속된 라이브러리를 설치할 수 있다고 한다. 그런데 공식 페이지에 다음과 같이 사용할 수 있다고 안내하지만 실행해보면 에러가 난다. 주피터노트북 환경에서 “!pip” 로 설치해도 제대로 실행되지 않는다.

1
pip install "pandas[performance, aws]>=2.0.0"

Anaconda 프롬프트에서 다음과 같이 설치 시도를 했으나 버전 인식을 못하는지 설치가 제대로 되지 않았다.
Pandas 2.0.0 설치 에러

아직은 명령어 지원이 수월하게 되지 않는 것으로 보인다.

성능 향상(Performance improvements)

데이터 유형별, 멀티인덱스, 파일 읽어오기 등 다양한 부분에서 성능 향상이 이루어졌으며 주요 내용은 다음과 같다.

  • nullable 데이터 유형에 대한 DataFrameGroupBy.median(), SeriesGroupBy.median(), DataFrameGroupBy.cumprod()의 성능 향상

  • nullable 데이터 유형에 대한 Series.value_counts()의 성능 향상

  • nullable 데이터 유형에 대해 .var(), .std()의 성능 향상

  • nullable 데이터 유형에 대해 Series.median()의 성능 향상

  • object 데이터 유형에 대한 DataFrameGroupBy.all(), DataFrameGroupBy.any(), SeriesGroupBy.all(), SeriesGroupBy.any()의 성능 향상

  • NumPy Array의 확장 데이터 유형에 대한 DataFrameGroupBy.mean(), SeriesGroupBy.mean(), DataFrameGroupBy.var(), SeriesGroupBy.var()의 성능 향상

  • NumPy Array의 확장 데이터 유형에 대한 Series.fillna()의 성능 향상

  • 정렬된 멀티인덱스 객체의 join 연산에서의 merge()DataFrame.join()의 성능 향상

  • 시간지역 오프셋(timezone offset)을 포함한 문자열도 이제 to_datetime()함수가 지원

    • 이 부분은 사실상 별도로 timedelta로 처리하기 때문에 큰 장점이 있는지 불명확하다.
      1
      2
      3
      4
      5
      6
      7
      # 1.5.3
      pd.to_datetime("2023-03-23 12:34:56+9:00")
      ## Timestamp('2023-03-23 12:34:56+0900', tz='pytz.FixedOffset(540)')

      # 2.0.0rc1
      pd.to_datetime("2023-03-23 12:34:56+9:00")
      ## Timestamp('2023-03-23 12:34:56+0900', tz='UTC+09:00')
  • DataFrame.loc()Series.loc() 에 튜플 기반의 멀티인덱스의 인덱싱 가능

    • 멀티인덱스에서 특정 레벨의 인덱스의 복수 인덱스 지정이 1.5.1 버전에선 오류가 났었는데 우선 다음과 같이 데이터를 준비한다.

      1
      2
      3
      df = pd.DataFrame([[1, 2], [3, 4], [5, 6], [7, 8]],
      index = [["A", "A", "B", "B"], ["C", "D", "C", "D"]])
      df
      0 1
      A C 1 2
      D 3 4
      B C 5 6
      D 7 8
    • 업데이트로 인해 새로 동작하는 코드는 다음과 같다.

      1
      2
      3
      4
      5
      6
      # 기존 코드도 올바르게 동작
      df.loc[("A", "D"), ]
      df.loc[(["A"], ["D"]), ]

      # 1.5.3에서 에러가 발생하던 코드
      df.loc[(["A"], ["C", "D"]), ]
      0 1
      A C 1 2
      D 3 4
  • 멀티인덱스 일부를 join할 때 DataFrame.join()의 성능 향상

  • 멀티인덱스에서 Series.rename()의 성능 향상

  • 범주형 데이터(categorical type)에 대해 Series.replace()의 성능 향상

  • 범주형 데이터(categorical type)에 DataFrame.GroupBy(), Series.GroupBy() 사용시 “sort=False”와 “observed=False” 설정에서의 성능 향상

  • read_sas()의 성능 향상

  • read_stata()의 “index_col” 파라미터의 기본값이 None으로 지정되었으며 이제 인덱스는 “RangeIndex”가 아닌 “Int64Index”로 지정됨

  • 복수 테이블에 read_html()를 사용하는 경우 성능 향상

  • “%Y%m%d” 유형에 to_datetime()를 사용하는 경우 성능 향상

  • 유추 가능한 형식의 데이터를 to_datetime()로 처리하는 경우 성능 향상

  • DataFrame.to_pickle(), Series.to_pickle()으로 “BZ2” 또는 “LZMA” 방식을 사용할 때 메모리 사용량 감소

  • to_numpy() 의 성능 향상

  • object가 아닌 데이터 유형에서 DataFrame.to_dict()Series.to_dict()의 성능 향상

  • date_parser와 시간대 오프셋이 섞여있는 데이터에 lambda 함수가 있는 to_datetime()read_csv()에 넘기는 경우 성능 향상

  • isna()isnull()의 성능 향상

  • 범주형 데이터(categorical type)에 사용하는 SeriesGroupBy.value_counts()의 성능 향상

  • DataFrame.to_json() 또는 Series.to_json()로 datetime와 timedelta 데이터 유형을 직렬화(serializing)하는 경우 발생하는 메모리 누수 보완

  • 여러 DataFrame.GroupBy() 메서드의 메모리 사용량 감소

  • DataFrame.round()의 자리수 지정 파라미터 관련 성능 향상

  • DataFrame.replace() 또는 Series.replace()에 큰 딕셔너리 객체를 사용하는 경우 성능 향상

API 호환 변경(Backwards incompatible API changes)

  • datetime64와 timedelta64 데이터 유형의 다양한 시간 해상도(resolution) 지원

    • 나노초 단위로 지정되었으나 이제 “s”, “ms”, “us”, “ns” 중 하나 선택 가능
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      # 이전 버전
      pd.Series(["2016-01-01"], dtype="datetime64[s]")
      ## 0 2016-01-01
      ## dtype: datetime64[ns]

      pd.Series(["2016-01-01"], dtype="datetime64[D]")
      ## 0 2016-01-01
      ## dtype: datetime64[ns]

      # 신규 버전
      pd.Series(["2016-01-01"], dtype="datetime64[s]")
      ## 0 2016-01-01
      ## dtype: datetime64[s]
  • 다양한 시간 해상도를 지원하도록 개선되어 .astype() 메서드 또한 이를 지원
    ※ “s”, “ms”, “us”, “ns”

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    # 이전 버전
    idx = pd.date_range("2016-01-01", periods=3)
    ser = pd.Series(idx)
    ser.astype("datetime64[s]")
    ## 0 2016-01-01
    ## 1 2016-01-02
    ## 2 2016-01-03
    ## dtype: datetime64[ns]

    # 신규 버전
    idx = pd.date_range("2016-01-01", periods=3)
    ser = pd.Series(idx)
    ser.astype("datetime64[s]")
    ## 0 2016-01-01
    ## 1 2016-01-02
    ## 2 2016-01-03
    ## dtype: datetime64[s]
  • 이제 .astype() 으로 일별 증분(timedelta64[D])을 지정할 수 없음

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    # 이전 버전
    idx = pd.timedelta_range("1 Day", periods=3)
    ser = pd.Series(idx)
    ser.astype("timedelta64[s]")
    ## 0 86400.0
    ## 1 172800.0
    ## 2 259200.0
    ## dtype: float64

    ser.astype("timedelta64[D]")
    ## 0 1.0
    ## 1 2.0
    ## 2 3.0
    ## dtype: float64

    # 신규 버전
    idx = pd.timedelta_range("1 Day", periods=3)
    ser = pd.Series(idx)
    ser.astype("timedelta64[s]")
    ## 0 86400.0
    ## 1 172800.0
    ## 2 259200.0
    ## dtype: float64

    ser.astype("timedelta64[D]")
    ## 에러!!!
  • .value_counts() 메서드에 이제 이름이 올바로 지정됨

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    # 이전 버전
    pd.Series(['quetzal', 'quetzal', 'elk'], name='animal').value_counts()
    ## quetzal 2
    ## elk 1
    ## Name: count, dtype: int64

    # 신규 버전
    pd.Series(['quetzal', 'quetzal', 'elk'], name='animal').value_counts()
    ## animal
    ## quetzal 2
    ## elk 1
    ## Name: count, dtype: int64
  • 비어있는 DataFrame 또는 Series 객체의 인덱스는 RangeIndex로 지정됨

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    # 이전 버전
    pd.Series().index
    ## Index([], dtype='object')

    pd.DataFrame().axes
    ## [Index([], dtype='object'), Index([], dtype='object')]

    # 신규 버전
    pd.Series().index
    ## RangeIndex(start=0, stop=0, step=1)

    pd.DataFrame().axes
    ## [RangeIndex(start=0, stop=0, step=1), RangeIndex(start=0, stop=0, step=1)]
  • 이제 날짜 형식이 일관된 형식으로 변환됨

    • 예전엔 각 원소를 적당히 유추해서 했으나 이제는 전체 원소를 일관된 형식으로 변환
    • to_datetime()의 “dayfirst” 인자에 True를 할당하여 날짜로 먼저 시작하는지 지정 가능
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      # 이전 버전
      ser = pd.Series(['13-01-2000', '12-01-2000'])
      pd.to_datetime(ser)
      ## 0 2000-01-13
      ## 1 2000-12-01
      ## dtype: datetime64[ns]

      # 신규 버전
      ser = pd.Series(['13-01-2000', '12-01-2000'])
      pd.to_datetime(ser) # 경고 발생
      ## 0 2000-01-13
      ## 1 2000-01-12
      ## dtype: datetime64[ns]

      ser = pd.Series(['13-01-2000', '12-01-2000'])
      pd.to_datetime(ser, dayfirst = True)
      ## 0 2000-01-13
      ## 1 2000-01-12
      ## dtype: datetime64[ns]

주요 버그 수정(Notable bug fixes)

  • DataFrameGroupBy.cumsum()DataFrameGroupBy.cumprod() 연산시 float으로 변환되는 대신 overflow 발생

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    # 이전 버전
    df = pd.DataFrame({"key": ["b"] * 7, "value": 625})
    df.groupby("key")["value"].cumprod()[5]
    ## 5.960464477539062e+16

    # 신규 버전
    df = pd.DataFrame({"key": ["b"] * 7, "value": 625})
    df.groupby("key")["value"].cumprod()
    ## 0 625
    ## 1 390625
    ## 2 244140625
    ## 3 152587890625
    ## 4 95367431640625
    ## 5 59604644775390625
    ## 6 359414837200037393
    ## Name: value, dtype: int64
  • datetime64이나 timedelta64 데이터 유형에서 해상도 지원부분 수정

지원 중단(Deprecations)

주로 데이터 타입과 관련된 지원 중단 소식이 많다. 기존 코드가 조금씩 더 길어질 것 같지만 코드를 일원화 하고 통일하려고 하는 움직임이 느껴진다.

  • Index.is_boolean() X → pandas.api.types.is_bool_dtype()
  • Index.is_integer() X → pandas.api.types.is_integer_dtype()
  • Index.is_floating() X → pandas.api.types.is_float_dtype()
  • Index.holds_integer() X → pandas.api.types.infer_dtype()
  • Index.is_numeric() X → pandas.api.types.is_any_real_numeric_dtype()
  • Index.is_categorical() X → pandas.api.types.is_categorical_dtype()
  • Index.is_object() X → pandas.api.types.is_object_dtype()
  • Index.is_interval() X → pandas.api.types.is_interval_dtype()

참고

NumPy - Extension array dtypes

NumPy Array의 확장 데이터 유형(Extension array dtypes)는 NumPy의 데이터 타입 시스템을 확장한 사용자 정의 데이터 타입(문자열, 날짜, 시간, 지리 정보 등)

Pandas - nullable dtype

Pandas에서 nullable data type은 “Nullable”이라고도 불리며, “nullable dtype”라는 용어로도 흔히 사용된다. 이는 Pandas 1.0 버전 이후 도입된 개념으로, 일반적인 데이터 유형의 확장으로 볼 수 있다.

기존의 Pandas 데이터 유형의 경우, 예를 들어 정수형 데이터 유형인 int64는 결측치(missing value)를 표현할 수 있는 값이 없기 때문에, 결측치가 있는 데이터를 다루기에는 제한이 있었다. 하지만 nullable dtype을 사용하면 결측치를 표현할 수 있다.

Pandas에서 nullable dtype으로 사용 가능한 데이터 유형에는 다음과 같은 것들이 있습니다.

  • 정수형 데이터 유형: Int8, Int16, Int32, Int64
  • 부호가 없는 정수형 데이터 유형: UInt8, UInt16, UInt32, UInt64
  • 부동소수점 데이터 유형: Float32, Float64
  • 불리언 데이터 유형: Boolean

nullable dtype을 사용하면, 해당 데이터 유형으로 생성한 Series나 DataFrame에서 결측치를 표현할 수 있는 특별한 값을 사용할 수 있습니다. 해당 값은 보통 NaN(Not a Number)으로 표시된다.

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×