Polars와 pandas 비교: 어떻게 다를까요?

Read this post in other languages:

지난 해에 Python DataFrame의 발전을 지켜보신 분이라면 대규모 데이터세트 작업용으로 설계된 강력한 DataFrame 라이브러리인 Polars를 들어보셨을 겁니다.


DataSpell에서 Polars를 사용해 보세요

Spark, DaskRay와 같은 다른 대용량 데이터세트 라이브러리와는 달리 Polars는 한 대의 시스템에서 사용되도록 설계되었기 때문에 pandas와 유사한 것으로 많이 비교됩니다. 그러나 Polars는 데이터 처리 방식이나 최적의 적용 분야 등과 같이 여러 중요한 부분에서 pandas와 차이점이 있습니다. 이 글에서는 두 DataFrame 라이브러리의 기술적인 차이점과 각각의 장단점을 알아봅니다.

이에 관해 Polars의 제작자인 Ritchie Vink의 설명을 자세히 들어보고 싶다면, 아래에서 인터뷰도 확인해 보세요!

pandas보다 Polars를 사용해야 하는 이유

한 마디로 성능 때문입니다. Polars는 처음부터 속도가 빠르도록 설계되어 pandas보다 공통 연산을 5~10배 정도 빠르게 처리할 수 있습니다. 또한, pandas보다 Polars 연산의 메모리 요구 사항이 훨씬 낮습니다. pandas는 연산을 처리하기 위해 데이터세트의 크기 대비 5~10배 정도의 RAM이 필요한 반면 Polars는 2~4배면 충분합니다.

Polars의 성능을 다른 DataFrame 라이브러리와 비교한 결과는 여기에서 확인할 수 있습니다. 결과를 보면 Polars는 공통 연산에서 pandas보다 10~100배 더 빠르고 전체적으로도 DataFrame 라이브러리 중에서 가장 빠른 편입니다. 게다가 Polars는 pandas에서 메모리 부족 오류가 발생하는 대규모 데이터세트도 처리할 수 있습니다.

Polars가 이렇게 빠른 이유는?

결과가 매우 인상적이다 보니 ‘한 대의 시스템에서 구동되는 Polars가 이런 성능을 어떻게 낼 수 있을까?’라는 궁금증이 드실 수도 있습니다. Polars 라이브러리는 처음부터 성능을 고려하여 설계되었으며, 이는 여러 수단을 통해서 달성되었습니다.

Rust로 작성

Polars에 관련해 가장 잘 알려진 사실 중 하나는 C나 C++만큼 빠른 로우레벨 언어인 Rust로 작성되었다는 점입니다. 반면에 pandas는 NumPy를 포함한 여러 Python 라이브러리를 기반으로 만들어졌습니다. NumPy의 핵심 부분은 C로 작성되어 있기는 하지만 Python이 범주형 데이터의 문자열 등과 같은 특정 타입을 메모리에서 처리하는 방식 때문에 근본적인 문제가 있어 이러한 타입을 처리할 때 성능이 저하됩니다(자세한 내용은 Wes McKinney가 작성한 이 블로그 게시물 참조).

Rust를 사용하는 것의 다른 장점 중 하나는 안전한 동시 실행입니다. Rust는 병렬 처리를 최대한 예측할 수 있도록 설계되었습니다. 그렇기 때문에 Polars는 다수의 열과 관련된 복잡한 쿼리에도 시스템의 모든 코어를 안전하게 사용할 수 있습니다. 이에 Ritchie Vink는 Polar의 성능을 보고 “굉장히 병렬적”이라고 평가하기도 했습니다. 따라서 Polars는 하나의 코어만 사용하여 연산을 처리하는 pandas보다 성능이 뛰어납니다. 올해 Nico Kreiling이 PyCon DE에서 멋지게 발표한 내용을 보면 Polars가 어떻게 이런 장점을 달성했는지 상세히 알아볼 수 있습니다.

Arrow 기반

Polars의 놀라운 성능에 기여하는 또 다른 요소는 언어에 독립적인 메모리 형식인 Apache Arrow입니다. Wes McKinney는 데이터의 크기가 커지면서 pandas에서 메모리 문제가 많이 발생하는 것을 보고 Arrow를 공동 개발하였습니다. 또한, Arrow는 올해 3월에 릴리스된 더 높은 성능의 pandas인 pandas 2.0의 백엔드로 사용되고 있습니다. 그러나 Arrow의 백엔드는 라이브러리별로 다릅니다. pandas 2.0은 PyArrow 기반이지만 Polars 팀은 자체 Arrow를 구현했습니다.

Arrow로 데이터 라이브러리를 만들 때의 가장 큰 장점은 상호 운용성입니다. Arrow는 여러 라이브러리에서 사용되는 메모리 내의 데이터 형식을 표준화하도록 설계되었으며, 아래에 나와 있듯이 다수의 주요 라이브러리와 데이터베이스에서 이미 사용되고 있습니다.

이러한 상호 운용성 덕분에 데이터 파이프라인의 여러 단계에 거쳐 데이터를 전달할 때 다른 형식으로 변환할 필요가 없으므로(즉, 데이터를 직렬화하거나 역직렬화할 필요가 없음) 성능이 높아집니다. 또한, 두 개의 프로세스가 데이터를 복사할 필요 없이 서로 공유할 수 있기 때문에 메모리 효율성이 향상됩니다. 직렬화/역직렬화가 데이터 워크플로에서 컴퓨팅 비용의 80~90%를 차지하는 것으로 추산되기 때문에 Arrow의 공통 데이터 형식은 Polars의 성능 향상에 크게 기여합니다.

Arrow는 pandas보다 더 광범위한 데이터 유형을 기본적으로 지원합니다. pandas는 Numpy 기반이기 때문에 정수 및 부동소수점 열을 처리하는 데는 뛰어나지만 다른 데이터 타입을 처리할 때는 원활하지 않습니다. 반면에 Arrow는 날짜와 시간, 부울, 바이너리는 물론 목록을 포함하는 열 등의 복잡한 열 타입도 정교하게 지원합니다. 여기에 더해서 누락된 데이터를 처리하려면 우회법을 사용해야 하는 NumPy와는 달리 Arrow는 기본적으로 처리할 수 있습니다.

마지막으로 Arrow는 열 형태의 데이터 저장소를 사용합니다. 따라서 어떤 데이터 형식을 사용하더라도 모든 열이 메모리상에서 연속되는 블록에 저장됩니다. 이렇게 하면 병렬 처리가 쉬워질 뿐만 아니라 데이터 검색도 빨라집니다.

쿼리 최적화

Polars의 핵심 성능 요소 중 다른 하나는 코드 평가 방식입니다. Pandas는 기본적으로 즉시 실행하는 방식을 사용하여 연산을 작성 순서대로 처리합니다. 반면에 Polars는 즉시 실행과 더불어 쿼리 최적화 도구가 필요한 모든 연산을 평가한 다음 코드를 실행하는 가장 효율적인 방식을 매핑하는 지연 실행도 지원합니다. 이러한 매핑에는 연산 실행 순서 변경이나 중복 연산 제거도 포함됩니다. Category의 ‘A’와 ‘B’ 각 카테고리에 대해 열 Number1의 중윗값을 구하는 다음의 표현식을 예시로 살펴보겠습니다.

(
df
.groupby(by = "Category").agg(pl.col("Number1").mean())
.filter(pl.col("Category").is_in(["A", "B"]))
)

이 표현식이 즉시 실행되면 groupby 연산이 전체 DataFrame에 대해 불필요하게 실행된 다음 Category로 필터링됩니다. 지연 실행 방식을 사용하면 DataFrame이 필터링된 다음 groupby가 필요한 데이터에 대해서만 실행됩니다.

표현력이 좋은 API

마지막으로 Polars는 표현력이 매우 좋은 API를 사용하므로, 실행하고 싶은 거의 모든 연산을 Polars 메서드로 표현할 수 있습니다. 반면에 복잡한 연산을 pandas에서 사용하려면 람다 식으로 apply 메서드에 전달해야 하는 경우가 많습니다. apply 메서드의 문제점은 DataFrame의 행을 한 번에 하나씩 순차적으로 처리하며 반복한다는 점입니다. 기본 제공되는 메서드를 사용할 수 있다면 열 단위로 작업할 수 있으며 SIMD라는 다른 형태의 병렬 처리 방식도 이용할 수 있습니다.

pandas를 사용해야 하는 경우는?

지금까지 들은 성능이 너무 좋아서 pandas는 이제 쓸 이유가 하나도 없는 것 아닌가 하는 의문이 드실 수도 있습니다. 하지만 아직은 그렇지 않습니다. Polars가 데이터 변환을 매우 효율적으로 처리할 수는 있지만, 머신러닝 파이프라인의 일부로 사용하거나 데이터 탐색 목적으로 사용하기에는 현재 최적의 선택지가 아닙니다. 이러한 영역에서는 pandas가 여전히 빛을 발합니다.

그 이유 중 하나는 Polars가 Arrow를 사용하기 때문에 다른 패키지와의 호환성이 뛰어나지만, 아직은 대다수의 Python 데이터 시각화 패키지나 scikit-learn 또는 PyTorch와 같은 머신러닝 라이브러리와 호환되지 않기 때문입니다. Polars DataFrame을 사용하여 직접 차트를 그릴 수 있는 유일한 예외는 Plotly입니다.

현재 논의 중인 솔루션은 Python DataFrame 상호 교환 프로토콜을 이러한 패키지에 사용하여 다양한 DataFrame 라이브러리를 지원하는 것입니다. 이렇게 하면 데이터 과학이나 머신러닝 워크플로에서 pandas 때문에 병목 현상이 발생할 일이 없습니다. 그러나 이는 비교적 최근에 나온 아이디어이고 이러한 프로젝트를 실행하려면 시간이 걸립니다.

Polars 및 pandas 도구

글을 다 읽으셨다면 분명 Polars를 직접 배우고 싶으실 겁니다! DataSpell과 PyCharm Professional 모두 Jupyter Notebook에서 pandas나 Polars로 작업할 때 훌륭한 도구입니다. 특히 pandas와 Polars DataFrame이 대화형으로 표시되어 데이터를 더욱 빠르고 편리하게 탐색할 수 있습니다.

저 개인적으로는 DataFrame의 모든 행과 열을 잘림 없이 스크롤하거나 클릭 한 번으로 DataFrame 값의 합계를 구하는 기능, DataFrame을 다양한 형식(Markdown 포함)으로 내보내는 기능이 정말 마음에 들었습니다!

아직 DataSpell을 사용해 본 적이 없다면 아래 링크를 사용하여 무료로 30일 동안 체험해 보세요.


지금 사용해 보세요!

게시물 원문 작성자

Jodie Burchell

Jodie Burchell

Dr. Jodie Burchell is the Developer Advocate in Data Science at JetBrains, and was previously a Lead Data Scientist at Verve Group Europe. She completed a PhD in clinical psychology and a postdoc in biostatistics, before leaving academia for a data science career. She has worked for 7 years as a data scientist in both Australia and Germany, developing a range of products including recommendation systems, analysis platforms, search engine improvements and audience profiling. She has held a broad range of responsibilities in her career, doing everything from data analytics to maintaining machine learning solutions in production. She is a long time content creator in data science, across conference and user group presentations, books, webinars, and posts on both her own and JetBrain’s blogs.

image description