워로디스

Pandas. value_counts(), idxmax(), mode()와 MultiIndex 정리 본문

Data Science/Pandas

Pandas. value_counts(), idxmax(), mode()와 MultiIndex 정리

워로디스 2026. 10. 19. 22:54

1. 핵심 개념: pandas는 입력 객체에 따라 동작이 달라진다

pandas에서 가장 헷갈리기 쉬운 부분 중 하나는 동일한 메서드라도 입력 객체가 Series인지 DataFrame인지에 따라 동작이 달라질 수 있다는 것이다.

특히 value_counts(), idxmax(), mode()를 이해하려면 다음 세 가지를 구분해야 한다.

  1. 입력 객체의 자료형: Series인가, DataFrame인가?
  2. 반환 객체의 자료형: Series인가, DataFrame인가?
  3. 반환 객체의 인덱스 구조: 일반 Index인가, MultiIndex인가?

이 세 가지를 이해하면 여러 메서드를 연결해서 사용하더라도 결과를 예측하기 쉬워진다.

2. value_counts(): 데이터의 등장 횟수를 계산하는 메서드

value_counts()는 데이터의 등장 횟수를 계산한다.

다만 Series와 DataFrame에서 집계하는 대상이 다르다.

예를 들어 다음과 같은 데이터프레임이 있다고 하자.

import pandas as pddf = pd.DataFrame({    '이름': ['철수', '영희', '철수', '민수', '철수'],    '점수': [90, 80, 90, 70, 80]})
인덱스 이름 점수
0 철수 90
1 영희 80
2 철수 90
3 민수 70
4 철수 80

2.1. Series에서 value_counts()

df['이름'].value_counts()

결과:

이름
철수    3
영희    1
민수    1
Name: count, dtype: int64

Series에서는 개별 값이 몇 번 등장했는지 계산한다.

여기서 중요한 점은 value_counts()의 결과가 다시 Series라는 것이다.

이 Series의 구조는 다음과 같다.

인덱스 (index) 값 (value)
철수 3
영희 1
민수 1

즉, 원래 데이터였던 철수, 영희, 민수는 인덱스가 되고, 등장 횟수인 3, 1, 1은 Series의 값이 된다.

2.2. DataFrame에서 value_counts()

이번에는 특정 열이 아니라 DataFrame 전체에 적용해 보자.

df.value_counts()

결과:

이름  점수
철수  90    2
민수  70    1
영희  80    1
철수  80    1
Name: count, dtype: int64

DataFrame에서는 각 행의 값 조합이 몇 번 등장했는지 계산한다.

예를 들어 ('철수', 90)이라는 조합은 두 번 등장했으므로 등장 횟수가 2가 된다.

여기서 주의할 점은 DataFrame에 value_counts()를 적용했지만 반환된 객체는 DataFrame이 아니라 Series라는 것이다.

type(df.value_counts())# pandas.core.series.Series

그리고 이 Series는 일반적인 단일 인덱스가 아니라 MultiIndex를 가진다.

3. MultiIndex: 여러 레벨로 구성된 인덱스

3.1. MultiIndex란?

MultiIndex는 하나의 인덱스를 여러 레벨로 구성한 것이다.

이름  점수
철수  90    2
민수  70    1
영희  80    1
철수  80    1
Name: count, dtype: int64

이 결과를 표로 나타내면 다음과 같다.

인덱스 레벨 0 (이름) 인덱스 레벨 1 (점수) 값 (count)
철수 90 2
민수 70 1
영희 80 1
철수 80 1

여기서 이름과 점수는 일반적인 데이터 열이 아니다.

하나의 인덱스를 구성하는 두 개의 레벨이다.

따라서 각 행의 인덱스는 튜플 형태로 표현할 수 있다.

('철수', 90)('민수', 70)('영희', 80)('철수', 80)

3.2. MultiIndex 확인하기

s = df.value_counts()print(s.index)

결과:

MultiIndex([('철수', 90),
            ('민수', 70),
            ('영희', 80),
            ('철수', 80)],
           names=['이름', '점수'])

names=['이름', '점수']는 각 인덱스 레벨의 이름을 의미한다.

3.3. MultiIndex를 이용한 데이터 접근

MultiIndex에서는 튜플을 이용해 특정 데이터를 가져올 수 있다.

s.loc[('철수', 90)]# 2

이는 ('철수', 90)이라는 인덱스에 대응하는 값이 2라는 의미다.

3.4. MultiIndex를 일반 열로 변환하기

MultiIndex가 불편하다면 reset_index()를 사용할 수 있다.

s.reset_index()

결과:

  이름 점수 count
0 철수 90 2
1 민수 70 1
2 영희 80 1
3 철수 80 1

기존의 인덱스 레벨인 이름, 점수가 일반 열로 변환되고, 기본 정수 인덱스가 새로 만들어진다.

반환 자료형도 Series에서 DataFrame으로 바뀐다.

type(s.reset_index())# pandas.core.frame.DataFrame

4. idxmax(): 최대값에 해당하는 인덱스를 반환하는 메서드

idxmax()를 이해하려면 max()와 구분해야 한다.

  • max()는 최대값 자체를 반환한다.
  • idxmax()는 최대값이 위치한 인덱스의 레이블을 반환한다.

4.1. 일반 Series에서 사용하기

s = pd.Series([10, 30, 20], index=['A', 'B', 'C'])
인덱스 값
A 10
B 30
C 20

 

s.max()# 30s.idxmax()# 'B'

값이 가장 큰 것은 30이지만 그 값에 대응하는 인덱스는 'B'이다.

따라서 idxmax()는 'B'를 반환한다.

idxmax()는 최대값이 아니라 최대값의 인덱스 레이블을 반환한다는 것이 핵심이다.

4.2. value_counts()와 결합하면 최빈값이 되는 이유

df['이름'].value_counts()

결과:

이름
철수    3
영희    1
민수    1
Name: count, dtype: int64

여기에 idxmax()를 적용하면 다음과 같다.

df['이름'].value_counts().idxmax()# '철수'

이 과정을 분해해 보면 다음과 같다.

counts = df['이름'].value_counts()counts.max()# 3counts.idxmax()# '철수'

value_counts()가 원래 데이터 값을 인덱스로 만들었으므로, 가장 큰 등장 횟수의 인덱스를 가져오면 자연스럽게 최빈값이 된다.

즉,

value_counts().idxmax() = 등장 횟수가 가장 많은 값의 인덱스 = 최빈값

이다.

4.3. MultiIndex에서 idxmax()를 사용하면?

s = df.value_counts()

결과:

이름  점수
철수  90    2
민수  70    1
영희  80    1
철수  80    1
Name: count, dtype: int64
s.max()# 2s.idxmax()# ('철수', 90)

최대값은 2이지만 그 값에 대응하는 인덱스는 ('철수', 90)이다.

따라서 idxmax()는 튜플을 반환한다.

이는 idxmax()의 동작이 달라진 것이 아니다.

인덱스의 구조가 달라졌기 때문에 반환값의 형태도 달라진 것이다.

5. mode(): 최빈값을 직접 구하는 메서드

pandas에는 최빈값을 직접 계산하는 mode()라는 메서드도 존재한다.

5.1. Series에서 mode()

df['이름'].mode()

결과:

0    철수
Name: 이름, dtype: object

mode()는 최빈값을 Series 형태로 반환한다.

따라서 최빈값 하나를 스칼라 값으로 가져오려면 다음과 같이 작성할 수 있다.

df['이름'].mode().iloc[0]# '철수'

5.2. mode()와 value_counts().idxmax()의 차이

두 방식은 최빈값이 하나일 때는 같은 결과를 얻을 수 있지만, 최빈값이 여러 개일 때 차이가 발생한다.

s = pd.Series(['A', 'A', 'B', 'B', 'C'])

각 값의 등장 횟수는 다음과 같다.

값 등장 횟수
A 2
B 2
C 1

이때 mode()는 다음과 같다.

s.mode()

결과:

0    A
1    B
dtype: object

반면 다음 코드는

s.value_counts().idxmax()

동률인 최빈값 중 하나만 반환한다. 이 예제에서는 'A'가 반환된다.

따라서 두 방식은 다음과 같이 구분할 수 있다.

메서드 최빈값이 여러 개일 때
mode() 모든 최빈값 반환
value_counts().idxmax() 그중 하나만 반환

5.3. DataFrame에서 mode()

df.mode()

DataFrame에서 mode()를 사용하면 각 열의 최빈값을 독립적으로 계산한다.

앞선 예제에서는 이름 열의 최빈값은 철수이고, 점수 열의 최빈값은 80과 90이다.

따라서 다음 결과가 나온다.

    이름    점수
0   철수   80.0
1   NaN   90.0

여기서 두 번째 행에 NaN이 있는 이유는 이름 열에는 최빈값이 하나뿐인데 점수 열에는 두 개가 있기 때문이다.

중요한 것은 df.mode()가 가장 많이 등장한 행의 조합을 찾는 것이 아니라 열별 최빈값을 구한다는 점이다.

행의 조합에 대한 최빈값은 df.value_counts().idxmax()로 구할 수 있다.

6. 메서드를 연결할 때 주의해야 하는 자료형 변화

pandas에서는 메서드들을 연속으로 호출하는 경우가 많다.

df['이름'].value_counts().idxmax()

이 코드를 제대로 이해하려면 각 단계에서 어떤 자료형이 나오는지 알아야 한다.

표현식 반환 자료형 의미
df DataFrame 전체 데이터
df['이름'] Series 이름 열
df[['이름']] DataFrame 이름 열만 포함한 DataFrame
df['이름'].value_counts() Series 이름별 등장 횟수
df.value_counts() Series 행 조합별 등장 횟수
df.value_counts().idxmax() tuple 가장 많이 등장한 행의 값 조합
df.value_counts().reset_index() DataFrame 집계 결과를 일반 열로 변환
df.mode() DataFrame 각 열의 최빈값
df['이름'].mode() Series 이름 열의 최빈값들

여기서 특히 주의할 부분은 다음 두 표현식이다.

df['이름']    # Series 반환df[['이름']]  # DataFrame 반환

겉보기에는 거의 같지만 자료형이 달라진다.

따라서 이후에 호출하는 메서드의 동작도 달라질 수 있다.

7. 추가로 알아두면 좋은 주의사항

7.1. value_counts()는 기본적으로 등장 횟수 내림차순으로 정렬한다

df['이름'].value_counts()

별도의 옵션을 지정하지 않으면 등장 횟수가 많은 값부터 출력된다.

df['이름'].value_counts(sort=False)

sort=False를 설정하면 등장 횟수 기준 정렬을 하지 않는다.

7.2. 결측값은 기본적으로 제외한다

df['이름'].value_counts()

기본적으로 NaN 값은 계산에서 제외한다.

결측값까지 포함하려면 다음과 같이 작성한다.

df['이름'].value_counts(dropna=False)

DataFrame에서도 dropna=False를 지정하면 결측값이 포함된 행 조합까지 계산할 수 있다.

7.3. idxmax()는 동률인 최대값을 모두 반환하지 않는다

idxmax()는 최대값이 여러 개 존재하더라도 그중 처음 나타나는 인덱스 레이블 하나만 반환한다.

따라서 최빈값이 여러 개일 수 있고 이를 모두 찾아야 한다면 mode()가 더 적절하다.

8. 요약

목적 사용 코드
특정 열의 값별 등장 횟수 df['이름'].value_counts()
동일한 행 조합의 등장 횟수 df.value_counts()
특정 열의 최빈값 하나 df['이름'].value_counts().idxmax()
특정 열의 최빈값 모두 df['이름'].mode()
가장 자주 등장한 행의 조합 하나 df.value_counts().idxmax()
가장 많이 등장한 행 조합의 횟수 df.value_counts().max()
각 열의 최빈값 df.mode()
MultiIndex를 일반 열로 변환 df.value_counts().reset_index()

가장 중요한 개념

첫째, value_counts()는 입력에 따라 집계 기준이 달라진다.

Series에서는 개별 값의 등장 횟수를 계산하고, DataFrame에서는 행의 값 조합별 등장 횟수를 계산한다.

둘째, idxmax()는 최대값 자체가 아니라 최대값에 대응하는 인덱스 레이블을 반환한다.

value_counts()가 원래 데이터 값을 인덱스로 사용하기 때문에, idxmax()를 연결하면 최빈값을 구할 수 있다.

셋째, MultiIndex는 여러 개의 인덱스가 따로 존재하는 것이 아니라 하나의 인덱스가 여러 레벨로 구성된 구조다.

따라서 MultiIndex에 idxmax()를 적용하면 여러 레벨의 레이블이 튜플 형태로 반환될 수 있다.

넷째, pandas에서는 메서드의 이름뿐 아니라 입력과 반환 객체의 자료형을 함께 확인해야 한다.

결국 다음 흐름을 이해하는 것이 핵심이다.

DataFrame
    │
    ├── df['이름']
    │       │
    │       ▼
    │     Series
    │       │
    │       ▼
    │   value_counts()
    │       │
    │       ▼
    │   Series (일반 Index)
    │       │
    │       ▼
    │     idxmax()
    │       │
    │       ▼
    │    '철수'
    │
    └── df.value_counts()
            │
            ▼
       Series (MultiIndex)
            │
            ▼
          idxmax()
            │
            ▼
       ('철수', 90)

즉, pandas의 동작을 예측하려면 각 메서드가 어떤 객체를 입력받고, 어떤 구조의 객체를 반환하는지 추적하는 습관이 중요하다.

반응형