python
Pandas Crosstab: 쉬운 분석을 위한 초보자 가이드

Pandas Crosstab: 쉬운 분석을 위한 초보자 가이드

MoeNagy Dev

판다스 크로스탭이란?

판다스 라이브러리의 crosstab() 함수는 교차표라고도 알려지는 조건부 빈도표를 생성하는 데 사용되는 강력한 도구입니다. 이 함수는 두 개 이상의 범주형 변수 간의 관계를 분석하기 위해 빈도 분포의 표 형태로 제공합니다.

crosstab() 함수는 하나 이상의 시리즈 또는 범주형 변수를 입력으로 사용하고, 행을 하나의 변수로, 열을 다른 변수로 하는 2차원 표를 생성합니다. 생성된 표는 입력 변수의 조합별로 빈도 또는 개수를 보여줍니다.

crosstab()의 주요 기능 및 사용 사례는 다음과 같습니다.

  • 빈도 분석: 범주형 변수의 다양한 조합의 빈도 또는 개수를 식별합니다.
  • 조건부 빈도표: 두 개 이상의 범주형 변수 간의 관계를 분석하기 위해 조건부 빈도표를 작성합니다.
  • 피벗 테이블: 피벗 테이블과 유사한 출력물을 생성하여 사용자 지정 및 분석을 더할 수 있습니다.
  • 조건부 확률: 변수 간의 조건부 확률을 계산합니다.
  • 데이터 탐색: 데이터 집합 내의 분포 및 관계를 탐색합니다.

간단한 크로스탭 생성하기

작업할 샘플 DataFrame을 생성해보겠습니다.

import pandas as pd
 
# 샘플 DataFrame 생성
data = {
    'Gender': ['남성', '여성', '남성', '여성', '남성', '여성'],
    'Age': ['젊은이', '젊은이', '노인', '노인', '젊은이', '노인'],
    'Count': [10, 8, 6, 12, 5, 9]
}
 
df = pd.DataFrame(data)

이제 crosstab() 함수를 사용하여 간단한 크로스탭을 생성할 수 있습니다.

pd.crosstab(df['Gender'], df['Age'])

이를 통해 DataFrame에서 'Gender'와 'Age'의 각 조합별 빈도를 보여주는 표가 출력됩니다.

Age     노인  젊은이
Gender        
남성     6   15
여성    12    8

표의 행은 'Gender' 변수를 나타내며, 열은 'Age' 변수를 나타냅니다. 표의 값은 각 조합의 빈도를 나타냅니다.

크로스탭 사용자 정의하기

crosstab() 함수를 사용자 정의할 수 있습니다. 사용 가능한 옵션 중 일부를 살펴보겠습니다.

행 및 열 레이블 지정하기

indexcolumns 매개변수를 사용하여 행 및 열에 사용자 정의 레이블을 제공할 수 있습니다.

pd.crosstab(df['Gender'], df['Age'], rownames=['Gender'], colnames=['Age'])

이렇게 하면 이전과 동일한 출력이 생성되지만 사용자 정의 행 및 열 레이블이 포함됩니다.

집계 함수 적용하기

기본적으로 crosstab() 함수는 변수 조합별 발생 횟수를 계산합니다. aggfunc 매개변수를 사용하여 이 동작을 변경하여 집계 함수를 적용할 수 있습니다.

pd.crosstab(df['Gender'], df['Age'], values=df['Count'], aggfunc=sum)

이렇게 하면 'Gender'와 'Age'의 각 조합별 'Count' 값을 합산하는 크로스탭이 생성됩니다.

누락된 값 처리하기

데이터에 누락된 값이 포함되어 있는 경우 marginsmargins_name 매개변수를 사용하여 해당 값을 처리할 수 있습니다.

pd.crosstab(df['Gender'], df['Age'], margins=True, margins_name='Total')

이렇게 하면 크로스탭에 'Total' 행과 열이 추가되어 각 행 및 열에 대한 총 개수가 제공됩니다.

고급 크로스탭 기법

다중 레벨 인덱스 작업

crosstab() 함수는 데이터의 다중 레벨 인덱스도 처리할 수 있습니다. 다중 레벨 인덱스가 있는 샘플 DataFrame을 생성해보겠습니다.

data = {
    ('Gender', ''): ['남성', '여성', '남성', '여성', '남성', '여성'],
    ('Age', ''): ['젊은이', '젊은이', '노인', '노인', '젊은이', '노인'],
    ('Count', ''): [10, 8, 6, 12, 5, 9]
}
 
df = pd.DataFrame(data)
df.columns = pd.MultiIndex.from_tuples(df.columns)

이제 다중 레벨 인덱스를 사용하여 크로스탭을 생성할 수 있습니다.

pd.crosstab(df[('Gender', '')], df[('Age', '')])

출력 결과는 행과 열 모두에 다중 레벨 인덱스가 있으며, 입력 데이터의 구조를 반영합니다.

크로스탭 결과 정규화하기

크로스탭 결과를 상대 빈도수로 표시하려면 normalize 매개변수를 사용하여 정규화할 수 있습니다.

pd.crosstab(df['Gender'], df['Age'], normalize='index')

이렇게 하면 크로스탭이 행 합계로 각 값을 나누어 행 백분율을 얻을 수 있습니다.

크로스탭 데이터 시각화하기

크로스탭 데이터를 시각화하려면 판다스나 Matplotlib 또는 Seaborn과 같은 다른 시각화 라이브러리에서 제공하는 다양한 플로팅 함수를 사용할 수 있습니다. 예를 들어:

import matplotlib.pyplot as plt
 
crosstab = pd.crosstab(df['Gender'], df['Age'])
crosstab.plot(kind='bar', figsize=(8, 6))
plt.title('성별과 나이의 크로스탭')
plt.xlabel('성별')
plt.ylabel('개수')
plt.show()

이렇게 하면 변수 간의 관계를 이해하는 데 도움이 되는 크로스탭 데이터의 막대 그래프가 생성됩니다.

크로스탭 필터링 및 정렬하기

기준에 따라 크로스탭 필터링하기

표준 판다스 인덱싱과 부울 마스킹 기법을 사용하여 특정 기준으로 크로스탭을 필터링할 수 있습니다.

crosstab = pd.crosstab(df['Gender'], df['Age'])
filtered_crosstab = crosstab.loc[crosstab['젊은이'] > 5]

이렇게 하면 '젊은이' 열 값이 5보다 큰 행만 포함된 새로운 크로스탭이 생성됩니다.

크로스탭의 행과 열 정렬하기

크로스탭의 행과 열을 정렬하려면 sort_index() 메서드를 사용할 수 있습니다.

crosstab = pd.crosstab(df['Gender'], df['Age'])
sorted_crosstab = crosstab.sort_index(axis=0, ascending=False)

이렇게 하면 crosstab의 행을 내림차순으로 정렬합니다.

필터링과 정렬을 결합하기

필터링과 정렬을 결합하여 crosstab 출력을 더욱 개인화할 수 있습니다:

crosstab = pd.crosstab(df['성별'], df['나이'])
filtered_sorted_crosstab = crosstab.loc[crosstab['젊음'] > 5].sort_index(axis=0, ascending=False)

이렇게 하면 먼저 '젊음' 열 값이 5보다 큰 행만 포함하는 crosstab을 필터링하고, 그런 다음 행을 내림차순으로 정렬합니다.

범주형 데이터로 크로스탭 생성

범주형 변수 처리

범주형 변수를 다룰 때는 해당 변수가 올바르게 범주형 데이터 유형으로 인코딩되어 있는지 확인하는 것이 중요합니다. astype() 메서드를 사용하여 열을 범주형 데이터 유형으로 변환할 수 있습니다:

df['성별'] = df['성별'].astype('category')
df['나이'] = df['나이'].astype('category')

범주형 변수에 대한 크로스탭 표시

범주형 변수를 설정한 후, 관계를 분석하기 위해 크로스탭을 생성할 수 있습니다:

pd.crosstab(df['성별'], df['나이'])

이렇게 하면 '성별'과 '나이' 범주형 변수에 대한 크로스탭이 표시됩니다.

범주형 데이터의 NaN 값 처리

데이터에 범주형 변수에 NaN(결측치) 값이 포함되어 있다면 dropna 매개변수를 사용하여 처리할 수 있습니다:

pd.crosstab(df['성별'], df['나이'], dropna=False)

이렇게 하면 크로스탭 출력에 NaN 값이 포함되어 결측 데이터를 분석할 수 있습니다.

시계열 데이터로 크로스탭 생성

시간 기반 데이터의 크로스탭 생성

데이터가 시간 관련 정보를 포함하고 있다면 crosstab() 함수를 사용하여 시간에 따른 관계를 분석할 수 있습니다. 날짜 열을 포함한 샘플 DataFrame을 생성해 봅시다:

data = {
    '날짜': ['2022-01-01', '2022-01-02', '2022-01-03', '2022-01-04', '2022-01-05', '2022-01-06'],
    '성별': ['남성', '여성', '남성', '여성', '남성', '여성'],
    '나이': ['젊음', '젊음', '노령', '노령', '젊음', '노령'],
    '개수': [10, 8, 6, 12, 5, 9]
}
 
df = pd.DataFrame(data)
df['날짜'] = pd.to_datetime(df['날짜'])

이제 '날짜' 열을 하나의 변수로 사용하여 크로스탭을 생성할 수 있습니다:

pd.crosstab(df['날짜'].dt.date, df['성별'])

이렇게 하면 DataFrame의 각 날짜에 대한 각 성별의 개수를 보여주는 크로스탭이 생성됩니다.

시간에 따른 추세와 패턴 분석

시간 기반 크로스탭의 추세와 패턴을 더 자세히 분석하려면 추가적인 pandas 함수나 시각화를 사용할 수 있습니다:

crosstab = pd.crosstab(df['날짜'].dt.date, df['성별'])
crosstab.plot(kind='line', figsize=(10, 6))
plt.title('시간별 성별 개수')
plt.xlabel('날짜')
plt.ylabel('개수')
plt.show()

이렇게 하면 시간에 따른 성별 개수의 선 그래프가 생성되어 데이터의 추세나 패턴을 파악할 수 있습니다.

날짜/시간 관련 작업 처리

시간 기반 데이터를 다룰 때, 연도, 월 또는 일별로 그룹화하는 등 다양한 날짜/시간 관련 작업을 수행해야 할 수도 있습니다. 이 경우 '날짜' 열의 dt 접근자를 사용하여 이러한 작업에 액세스할 수 있습니다:

pd.crosstab(df['날짜'].dt.month, df['성별'])

이렇게 하면 데이터에서 연도별로 성별의 개수를 보여주는 크로스탭이 생성됩니다.

다른 pandas 함수와 크로스탭 결합

크로스탭과 groupby() 함수 통합

crosstab() 함수를 groupby() 함수와 결합하여 더 복잡한 분석을 수행할 수 있습니다. 예를 들어, 데이터를 먼저 변수로 그룹화한 다음 그룹화된 데이터에 대해 크로스탭을 생성할 수 있습니다:

grouped_df = df.groupby(['성별', '나이'])
pd.crosstab(grouped_df.groups.keys(), df['날짜'].dt.date)

이렇게 하면 데이터에서 각 '성별'과 '나이' 조합에 대한 각 날짜의 개수를 보여주는 크로스탭이 생성됩니다.

크로스탭과 pivot_table() 함수 결합

crosstab() 함수는 pivot_table() 함수와 결합하여 더 고급적인 데이터 분석을 수행할 수도 있습니다:

pivot_table = pd.pivot_table(df, index=['성별', '나이'], columns='날짜', values='개수', aggfunc='sum')

이렇게 하면 다른 날짜별로 각 '성별'과 '나이' 조합의 '개수'의 합을 보여주는 피벗 테이블이 생성됩니다.

크로스탭에 대한 다른 pandas 함수 탐색

crosstab()은 강력한 도구이지만, crosstab()과 함께 사용하거나 crosstab() 대안으로 사용할 수 있는 다른 pandas 함수도 있습니다. 예를 들어:

  • value_counts(): Series의 고유 값의 빈도를 가져옵니다.
  • pivot(): 스프레드시트 스타일의 피벗 테이블을 DataFrame으로 생성합니다.
  • melt(): 넓은 형식의 DataFrame을 긴 형식으로 변환합니다.
  • cut()qcut(): 연속적인 데이터를 구간으로 나눕니다.

이러한 함수를 탐색하면 데이터 분석 도구킷을 확장하고 특정 사용 사례에 가장 적합한 접근 방식을 찾을 수 있습니다.

함수

함수는 Python에서 기본 개념으로서 일련의 명령을 캡슐화하여 코드 전체에서 재사용할 수 있도록 해줍니다. 함수는 입력 매개변수를 사용하고 일련의 작업을 수행한 다음 결과를 반환할 수 있습니다.

다음은 사각형의 면적을 계산하는 간단한 함수의 예입니다:

def calculate_area(length, width):
    area = length * width
    return area
 
# 함수 호출 및 결과 출력
result = calculate_area(5, 10)
print(f"사각형의 면적은 {result} 제곱 단위입니다.")

이 예제에서 calculate_area() 함수는 lengthwidth라는 두 개의 매개변수를 사용하고 계산된 면적을 반환합니다. 그런 다음 함수를 호출하고 결과를 result 변수에 저장하고 콘솔에 출력합니다.

함수에는 기본 매개변수 값도 있을 수 있으며, 이를 사용하여 인수를 모두 제공하지 않고 함수를 호출할 수 있습니다:

def greet(name, message="안녕하세요"):
    print(f"{message}, {name}!")
 
greet("Alice")  # 출력: 안녕하세요, Alice!
greet("Bob", "Hi")  # 출력: 안녕, Bob!
 

이 예제에서 greet() 함수는 message 매개변수에 대해 "Hello"라는 기본값을 가지므로, name 인자만으로 함수를 호출하면 기본 메시지가 사용됩니다.

모듈과 패키지

Python의 모듈화된 디자인을 통해 코드를 재사용 가능한 컴포넌트인 모듈로 구성할 수 있습니다. 모듈은 함수, 클래스 및 변수를 포함한 Python 파일로, 다른 코드의 일부에서 가져와 사용할 수 있습니다.

math_utils.py라는 간단한 모듈을 생성하는 예제를 살펴보겠습니다:

def add(a, b):
    return a + b
 
def subtract(a, b):
    return a - b
 
def multiply(a, b):
    return a * b
 
def divide(a, b):
    return a / b

그런 다음 다른 Python 파일에서 이 모듈의 함수를 가져와 사용할 수 있습니다:

import math_utils
 
result = math_utils.add(5, 3)
print(result)  # 출력: 8
 
result = math_utils.subtract(10, 4)
print(result)  # 출력: 6

모듈은 또한 패키지로 구성될 수도 있으며, 패키지는 여러 모듈을 포함하는 디렉토리입니다. 이를 통해 코드를 계층 구조로 구성하여 관리하기 쉽게 할 수 있습니다.

패키지 구조의 예를 살펴보겠습니다:

my_package/
    __init__.py
    math/
        __init__.py
        operations.py
        geometry.py
    data/
        __init__.py
        file_utils.py
        database_utils.py

이 예제에서 my_package 패키지는 mathdata 두 개의 하위 패키지를 포함하고 있습니다. 각 하위 패키지에는 고유한 모듈 집합이 있으며, __init__.py 파일을 사용하여 Python이 이러한 디렉토리를 패키지로 인식할 수 있도록 합니다.

그런 다음 패키지 내 모듈에서 함수를 가져와 사용할 수 있습니다:

from my_package.math.operations import add, subtract
from my_package.data.file_utils import read_file
 
result = add(5, 3)
print(result)  # 출력: 8
 
data = read_file("data.txt")
print(data)

객체 지향 프로그래밍 (OOP)

객체 지향 프로그래밍 (OOP)은 클래스의 인스턴스인 객체를 생성하는 데 중점을 둔 프로그래밍 패러다임입니다. 클래스는 객체의 구조와 동작을 정의하며, 객체는 서로 상호작용하여 복잡한 문제를 해결할 수 있습니다.

간단한 사람을 나타내는 클래스의 예를 살펴보겠습니다:

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
 
    def greet(self):
        print(f"안녕하세요, 저는 {self.name}이고 {self.age}살입니다.")
 
# Person 객체를 생성하고 greet 메소드를 호출합니다.
person = Person("Alice", 30)
person.greet()  # 출력: 안녕하세요, 저는 Alice이고 30살입니다.

이 예에서 Person 클래스에는 두 개의 속성 (nameage)과 하나의 메소드 (greet())가 있습니다. 새 Person 객체를 생성할 때 __init__() 메소드를 사용하여 속성의 초기값을 설정할 수 있습니다. __init__()은 생성자라고 불리는 특수한 메소드입니다.

기본 클래스로부터 상속을 받아 서브클래스를 생성하여 기본 클래스의 기능을 확장할 수도 있습니다:

class Student(Person):
    def __init__(self, name, age, grade):
        super().__init__(name, age)
        self.grade = grade
 
    def study(self):
        print(f"{self.name}은(는) {self.grade} 학년을 공부하고 있습니다.")
 
# Student 객체를 생성하고 메소드를 호출합니다.
student = Student("Bob", 15, "10th")
student.greet()  # 출력: 안녕하세요, 저는 Bob이고 15살입니다.
student.study()  # 출력: Bob은(는) 10th 학년을 공부하고 있습니다.

이 예에서 Student 클래스는 Person 클래스를 상속 받으며 grade 속성과 study() 메소드를 추가합니다. Student 클래스의 __init__() 메소드에서는 super() 함수를 사용하여 Person 클래스의 __init__() 메소드를 호출하여 nameage 속성을 초기화합니다.

예외 처리

Python의 예외 처리 메커니즘을 사용하여 코드에서 예기치 않은 상황을 처리하고 오류를 우아하게 처리할 수 있습니다. 프로그램 실행 중에 오류가 발생하면 예외가 발생하고, 이러한 예외를 처리하고 처리할 수 있는 코드를 작성할 수 있습니다.

ZeroDivisionError 예외를 처리하는 예를 살펴보겠습니다:

def divide(a, b):
    try:
        result = a / b
        return result
    except ZeroDivisionError:
        print("오류: 0으로 나눌 수 없습니다.")
        return None
 
print(divide(10, 2))  # 출력: 5.0
print(divide(10, 0))  # 출력: 오류: 0으로 나눌 수 없습니다.

이 예에서 divide() 함수는 try-except 블록을 사용하여 ZeroDivisionError 예외를 처리합니다. 나눗셈 연산이 예외를 발생시키면 except 블록의 코드가 실행되며, 콘솔에 메시지가 출력됩니다. 나눗셈이 성공하면 결과가 반환됩니다.

사용자 정의 예외를 내장된 Exception 클래스에서 상속받아 새로운 클래스를 만들어 직접 예외를 정의할 수도 있습니다:

class NegativeNumberError(Exception):
    pass
 
def square_root(number):
    if number < 0:
        raise NegativeNumberError("오류: 음수의 제곱근을 계산할 수 없습니다.")
    return number ** 0.5
 
try:
    print(square_root(16))  # 출력: 4.0
    print(square_root(-4))
except NegativeNumberError as e:
    print(e)  # 출력: 오류: 음수의 제곱근을 계산할 수 없습니다.

이 예에서 square_root() 함수는 입력 숫자가 음수인 경우 사용자 정의 NegativeNumberError 예외를 발생시킵니다. try-except 블록은 예외를 catch하고 오류 메시지를 출력합니다.

결론

이 Python 튜토리얼에서는 함수, 모듈 및 패키지, 객체 지향 프로그래밍 및 예외 처리와 같은 Python의 중급 수준의 다양한 개념에 대해 배웠습니다. 이러한 주제는 보다 복잡하고 견고한 Python 애플리케이션 구축에 필수적입니다. 기본 작성 형태를 제공하는 이 자습서에서 파이썬 기술을 향상시키는 가장 좋은 방법은 코드를 작성하고 문제를 해결해 보는 것입니다. 이 자습서에서 제공된 예제를 실험해 보고 이러한 개념을 자신의 프로젝트에 적용해 보세요. 또한 파이썬 라이브러리와 프레임워크의 방대한 생태계를 계속 탐색하면 파이썬 프로그램의 기능을 크게 확장할 수 있습니다.

코드를 즐겁게 작성해 보세요!

MoeNagy Dev