피벗

피벗 (Pivots)

피벗은 데이터프레임의 칼럼을 축으로 돌려서, 원하는 집계를 수행하는 변형이에요. 이 섹션에서는 피벗이 어떤 구조로 동작하는지, 그리고 지연(lazy) 평가에서 왜 특별한 주의가 필요한지를 함께 살펴볼게요.

출처: 공식문서

DataFrame에서 칼럼을 피벗하고 다음 집계 중 하나를 수행할 수 있어요:

  • first
  • last
  • sum
  • min
  • max
  • mean
  • median
  • len

피벗 연산은 하나 또는 여러 칼럼의 그룹 바이(이것이 새 y축이 됨), 피벗될 칼럼(이것이 새 x축이 됨), 그리고 하나의 집계로 구성됩니다.

데이터셋 (Dataset)

import polars as pl

df = pl.DataFrame(
    {
        "foo": ["A", "A", "B", "B", "C"],
        "N": [1, 2, 2, 4, 2],
        "bar": ["k", "l", "m", "n", "o"],
    }
)
print(df)
shape: (5, 3)
┌─────┬─────┬─────┐
│ foo ┆ N   ┆ bar │
│ --- ┆ --- ┆ --- │
│ str ┆ i64 ┆ str │
╞═════╪═════╪═════╡
│ A   ┆ 1   ┆ k   │
│ A   ┆ 2   ┆ l   │
│ B   ┆ 2   ┆ m   │
│ B   ┆ 4   ┆ n   │
│ C   ┆ 2   ┆ o   │
└─────┴─────┴─────┘

즉시 실행 (Eager)

out = df.pivot("bar", index="foo", values="N", aggregate_function="first")
print(out)
shape: (3, 6)
┌─────┬──────┬──────┬──────┬──────┬──────┐
│ foo ┆ k    ┆ l    ┆ m    ┆ n    ┆ o    │
│ --- ┆ ---  ┆ ---  ┆ ---  ┆ ---  ┆ ---  │
│ str ┆ i64  ┆ i64  ┆ i64  ┆ i64  ┆ i64  │
╞═════╪══════╪══════╪══════╪══════╪══════╡
│ A   ┆ 1    ┆ 2    ┆ null ┆ null ┆ null │
│ B   ┆ null ┆ null ┆ 2    ┆ 4    ┆ null │
│ C   ┆ null ┆ null ┆ null ┆ null ┆ 2    │
└─────┴──────┴──────┴──────┴──────┴──────┘

지연 실행 (Lazy)

Polars의 LazyFrame는 항상 계산의 스키마를 정적으로(쿼리를 수집하기 전에) 알아야 해요. 그런데 피벗의 출력 스키마는 데이터에 의존해서, 쿼리를 실행하지 않고는 스키마를 결정할 수 없습니다.

Polars가 Spark처럼 이 사실을 추상화해 줄 수도 있었겠지만, Polars는 여러분이 샷건으로 자기 발을 쏘게 두고 싶지 않아요. 그 비용은 처음부터 분명히 알려야 한다는 거죠.

피벗과 함께 지연 평가를 쓰고 싶다면 두 가지 선택지가 있어요. 피벗을 수행하기 전에 DataFrame을 수집하거나(지연 평가의 이점을 희생), on_columns 파라미터를 미리 지정해서 결과 스키마를 정적으로 선언하면 됩니다.

q = (
    df.lazy()
    .collect()
    .pivot(index="foo", on="bar", values="N", aggregate_function="first")
    .lazy()
)
out = q.collect()
print(out)
shape: (3, 6)
┌─────┬──────┬──────┬──────┬──────┬──────┐
│ foo ┆ k    ┆ l    ┆ m    ┆ n    ┆ o    │
│ --- ┆ ---  ┆ ---  ┆ ---  ┆ ---  ┆ ---  │
│ str ┆ i64  ┆ i64  ┆ i64  ┆ i64  ┆ i64  │
╞═════╪══════╪══════╪══════╪══════╪══════╡
│ A   ┆ 1    ┆ 2    ┆ null ┆ null ┆ null │
│ B   ┆ null ┆ null ┆ 2    ┆ 4    ┆ null │
│ C   ┆ null ┆ null ┆ null ┆ null ┆ 2    │
└─────┴──────┴──────┴──────┴──────┴──────┘
q = df.lazy().pivot(
    index="foo",
    on="bar",
    on_columns=["k", "l", "m", "n", "o"],
    values="N",
    aggregate_function="first",
)
out = q.collect()
print(out)
shape: (3, 6)
┌─────┬──────┬──────┬──────┬──────┬──────┐
│ foo ┆ k    ┆ l    ┆ m    ┆ n    ┆ o    │
│ --- ┆ ---  ┆ ---  ┆ ---  ┆ ---  ┆ ---  │
│ str ┆ i64  ┆ i64  ┆ i64  ┆ i64  ┆ i64  │
╞═════╪══════╪══════╪══════╪══════╪══════╡
│ C   ┆ null ┆ null ┆ null ┆ null ┆ 2    │
│ B   ┆ null ┆ null ┆ 2    ┆ 4    ┆ null │
│ A   ┆ 1    ┆ 2    ┆ null ┆ null ┆ null │
└─────┴──────┴──────┴──────┴──────┴──────┘

더 알아보기 (Learn more)

  • 피벗과 반대 방향으로, 넓은 데이터프레임을 긴(long) 형태로 재구성하고 싶다면 언피벗 (Unpivots) 문서를 참고하세요.