python
Pandas Unstack: 데이터 재구조화를 위한 초보자 가이드

Pandas Unstack: 데이터 재구조화를 위한 초보자 가이드

MoeNagy Dev

판다스 unstack 이해

판다스 unstack 설명

pandas unstack이란 무엇인가요?

unstack()은 판다스 기능으로서, 데이터프레임을 길게(stack) 구성된 형식에서 넓게(unstack) 구성된 형식으로 변환합니다. 이는 다중 레벨 열 인덱스를 가져와 "unstack"하여 하나 이상의 인덱스 레벨을 새로운 열로 만든 새로운 데이터프레임을 생성합니다.

pivot과 melt와 어떤 차이가 있나요?

unstack() 함수는 pivot() 함수와 유사하지만 다른 목적으로 사용됩니다. pivot() 함수는 데이터를 길게(stack) 구성된 형식에서 넓게(unstack) 구성된 형식으로 변환하는 데 사용되지만, unstack() 함수는 데이터를 넓게(unstack) 구성된 형식에서 길게(stack) 구성된 형식으로 변환하는 데 사용됩니다.

반면에 melt() 함수는 unstack() 함수의 반대로, 넓게(unstack) 구성된 형식에서 길게(stack) 구성된 형식으로 데이터를 변환하는 데 사용됩니다.

언제 판다스 unstack을 사용해야 하나요?

unstack()은 데이터프레임에 다중 레벨 열 인덱스가 있는 경우에 사용해야 합니다. 열 인덱스의 레벨이 데이터프레임의 새로운 열이 되는 넓게(unstack) 구성된 형식으로 변환하려는 경우입니다.

데이터 준비

필요한 라이브러리 가져오기

import pandas as pd
import numpy as np

샘플 데이터프레임 생성하기

# 샘플 데이터프레임 생성하기
data = {
    ('Store A', 'Sales'): [100, 120, 80, 90, 110],
    ('Store A', 'Profit'): [20, 25, 15, 18, 22],
    ('Store B', 'Sales'): [150, 180, 120, 160, 200],
    ('Store B', 'Profit'): [30, 35, 25, 32, 40]
}
 
df = pd.DataFrame(data)

데이터프레임의 구조 살펴보기

print(df)
           (Store A, Sales)  (Store A, Profit)  (Store B, Sales)  (Store B, Profit)
0                      100                 20               150                 30
1                      120                 25               180                 35
2                       80                 15               120                 25
3                       90                 18               160                 32
4                      110                 22               200                 40

위와 같이 데이터프레임은 다중 레벨 열 인덱스를 가지며, 첫 번째 레벨은 상점을 나타내고 두 번째 레벨은 메트릭(매출 또는 이익)을 나타냅니다.

판다스 unstack의 기본

단일 레벨 인덱스 unstack하기

단일 레벨 인덱스를 unstack하기 위해, 인자 없이 unstack() 함수를 사용할 수 있습니다:

df_unstacked = df.unstack()
print(df_unstacked)
                 Sales        Profit
                 Store A Store B Store A Store B
0                    100     150       20       30
1                    120     180       25       35
2                     80     120       15       25
3                     90     160       18       32
4                    110     200       22       40

결과적으로, 데이터프레임은 상점 이름을 열 인덱스로 가지며, 원래의 열 이름(매출과 이익)이 행 인덱스가 되었습니다.

다중 레벨 인덱스 unstack하기

만약 데이터프레임이 다중 레벨 열 인덱스를 가지고 있다면, unstack할 레벨을 지정할 수 있습니다:

df_unstacked = df.unstack(level=0)
print(df_unstacked)
                  (Sales, Store A)  (Sales, Store B)  (Profit, Store A)  (Profit, Store B)
0                             100               150                 20                 30
1                             120               180                 25                 35
2                              80               120                 15                 25
3                              90               160                 18                 32
4                             110               200                 22                 40

이 경우, 상점 이름이 열 인덱스로 변환되고, 원래의 열 이름(매출과 이익)은 다중 레벨 열 인덱스의 일부가 되었습니다.

결과적인 DataFrame 구조 이해하기

unstack된 데이터프레임은 다중 레벨 열 인덱스를 가지고 있으며, 첫 번째 레벨은 원래의 열 이름을 나타내고, 두 번째 레벨은 이전에 열 인덱스에 있던 값들을 나타냅니다.

이러한 구조는 특정 유형의 데이터 분석과 시각화에 유용합니다. 데이터를 다양한 방법으로 쉽게 접근하고 조작할 수 있기 때문입니다.

판다스 unstack에서 결측 데이터 처리

NaN 값 처리

원본 데이터프레임에 결측값이 있으면, unstack() 함수는 결과적인 데이터프레임에 NaN 값을 도입합니다:

# 샘플 데이터프레임에 결측값을 추가합니다
data = {
    ('Store A', 'Sales'): [100, 120, 80, np.nan, 110],
    ('Store A', 'Profit'): [20, 25, 15, 18, 22],
    ('Store B', 'Sales'): [150, 180, 120, 160, 200],
    ('Store B', 'Profit'): [30, 35, 25, 32, np.nan]
}
 
df = pd.DataFrame(data)
df_unstacked = df.unstack()
print(df_unstacked)
                 Sales        Profit
                 Store A Store B Store A Store B
0                    100     150       20     30.0
1                    120     180       25     35.0
2                     80     120       15     25.0
3                    NaN     160       18     32.0
4                    110     200       22      NaN

원본 데이터프레임의 결측값이 unstack된 데이터프레임에 그대로 유지되었음을 볼 수 있습니다.

결측값 채우기

결측값을 처리하기 위해, fillna() 메서드를 사용하여 특정 값으로 대체할 수 있습니다:

df_unstacked = df.unstack().fillna(0)
print(df_unstacked)
                 Sales        Profit
                 Store A Store B Store A Store B
0                    100     150       20       30
1                    120     180       25       35
2                     80     120       15       25
3                      0     160       18       32
4                    110     200       22        0
file: 0 100 150 20 30
1    120 180 25 35
2    80  120 15 25
3    0   160 18 32
4    110 200 22 0

이 예제에서는 누락된 값을 0으로 채웁니다.

채우기 값 지정

열 mean 또는 median과 같은 다른 채우기 값도 지정할 수 있습니다.

# 열의 mean 값으로 누락된 값을 채움
df_unstacked = df.unstack().fillna(df.mean())
print(df_unstacked)
                 Sales        Profit
                 Store A Store B Store A Store B
0                    100     150       20     32.5
1                    120     180       25     32.5
2                     80     120       15     32.5
3                    95.0     160       18     32.5
4                    110     200       22     22.0

이 예제에서는 누락된 값을 해당 열의 평균으로 채웁니다.

pandas unstack으로 고급 기법

특정 수준의 개열 인덱스를 언스택

모든 수준을 언스택하는 대신, 열 인덱스의 특정 수준을 언스택할 수도 있습니다:

# 두 번째 수준의 열 인덱스 언스택
df_unstacked = df.unstack(level=1)
print(df_unstacked)
                   Sales   Profit
Store A  0            100       20
         1            120       25
         2             80       15
         3            NaN       18
         4            110       22
Store B  0            150       30
         1            180       35
         2            120       25
         3            160       32
         4            200       NaN

이 경우, 가게 이름이 행 인덱스가 되고, 원래 열 이름 (Sales와 Profit)이 열 인덱스가 됩니다.

다른 pandas 연산과 언스택 결합

unstack() 함수를 reset_index() 또는 rename()과 결합하여 데이터를 더 조작할 수도 있습니다:

# 언스택하고 인덱스 재설정
df_unstacked = df.unstack().reset_index()
print(df_unstacked)
  level_0 level_1   0         1
0  Store A  Sales  100       20
1  Store A  Sales  120       25
2  Store A  Sales   80       15
3  Store A  Sales  NaN       18
4  Store A  Sales  110       22
5  Store B  Sales  150       30
6  Store B  Sales  180       35
7  Store B  Sales  120       25
8  Store B  Sales  160       32
9  Store B  Sales  200       NaN

이 예제에서는 DataFrame을 언스택하고 인덱스를 재설정하여 언스택된 값을 한 개의 열로 갖는 새로운 DataFrame을 만듭니다.

언스택 후 인덱스 재설정

언스택 후 인덱스를 재설정하려면 reset_index() 메소드를 사용할 수 있습니다:

# 언스택하고 인덱스 재설정
df_unstacked = df.unstack().reset_index()
print(df_unstacked)
  level_0 level_1   0         1
0  Store A  Sales  100       20
1  Store A  Sales  120       25
2  Store A  Sales   80       15
3  Store A  Sales  NaN       18
4  Store A  Sales  110       22
5  Store B  Sales  150       30
6  Store B  Sales  180       35
7  Store B  Sales  120       25
8  Store B  Sales  160       32
9  Store B  Sales  200       NaN

이렇게 하면 인덱스가 재설정되어 새로운 DataFrame을 만들며, 언스택된 값을 한 개의 열로 갖습니다. 원래 인덱스 수준은 이제 DataFrame의 열이 됩니다.

언스택된 데이터 시각화

히트맵 생성

언스택된 데이터 시각화의 한 가지 방법은 seaborn 라이브러리를 사용하여 히트맵을 생성하는 것입니다:

import seaborn as sns
import matplotlib.pyplot as plt
 
# DataFrame 언스택
df_unstacked = df.unstack()
 
# 히트맵 생성
plt.figure(figsize=(8, 6))
sns.heatmap(df_unstacked, annot=True, cmap="YlOrRd")
plt.title("상점별 판매량 및 이윤")
plt.show()

이렇게 하면 각 상점의 판매량과 이윤 데이터를 시각화하는 히트맵이 생성됩니다.

피벗 테이블 생성

언스택된 데이터에서 pivot_table() 함수를 사용하여 피벗 테이블을 생성할 수도 있습니다:

# 피벗 테이블 생성
pivot_table = df.pivot_table(index=['Store'], columns=['Metric'], values=['Value'])
print(pivot_table)
                 Value
Metric   Profit  Sales
Store A       20   100
        25    120
        15     80
        18     NaN
        22    110
Store B       30   150
        35    180
        25    120
        32    160
        NaN   200

이 피벗 테이블은 상점 이름을 행 인덱스로, 메트릭 이름을 열 인덱스로 가지며, 해당 값은 셀에 포함됩니다.

언스택된 데이터 플로팅

플로팅 데이터를 언스택된 데이터로 직접 만들 수도 있습니다. 막대 그래프나 선 그래프를 생성하는 것과 같은 방법도 있습니다:

# 언스택된 데이터 플로팅
df_unstacked.plot(kind="bar", figsize=(10, 6))
plt.title("상점별 판매량 및 이윤")
plt.xlabel("상점")
plt.ylabel("값")
plt.show()

이렇게 하면 각 상점의 판매량과 이윤 값을 보여주는 막대 그래프가 생성됩니다.

pandas unstack의 실제 응용

판매 데이터 분석

언스택은 특히 다중 수준 열 인덱스가 있는 판매 데이터를 분석할 때 유용합니다. 언스택된 데이터를 사용하여 피벗 테이블, 히트맵 또는 다른 시각화를 생성하여 다른 상점, 제품 또는 시기별로 판매 동향과 성과를 더 잘 이해할 수 있습니다.

시계열 데이터 재구성

unstack()은 시간과 다른 차원 (예: 위치, 제품)을 가진 다중 수준 인덱스가 있는 시계열 데이터를 재구성하는 데 유용할 수 있습니다. 데이터를 언스택하여 특정 유형의 분석과 시각화에 더 적합한 넓은 형식의 DataFrame을 만들 수 있습니다.

설문 조사 데이터 처리

설문 조사 데이터의 경우, 참가자별로 다른 질문에 대한 응답이 있는 경우 unstack()을 사용하여 데이터를 긴 형식에서 넓은 형식으로 변환하여 다른 설문 조사 질문 간의 관계를 분석하기가 더 쉬워집니다.

문제 해결 및 최적화 사례

일반적인 문제 및 오류 메시지

unstack()과 관련된 일반적인 문제점은 NaN값을 도입할 수 있다는 점입니다.

함수

함수는 특정 작업을 수행하는 재사용 가능한 코드 블록입니다. 함수는 입력 매개변수를 받아들이고 작업을 수행하며 값을 반환할 수 있습니다. 함수는 코드를 구조화하고 모듈화하여 코드를 더 읽기 쉽고 유지 보수 가능하도록 도와줍니다.

다음은 원의 넓이를 계산하는 간단한 함수의 예입니다:

def calculate_circle_area(radius):
    """
    원의 넓이를 계산합니다.
 
    Args:
        radius (float): 원의 반지름.
 
    Returns:
        float: 원의 넓이.
    """
    pi = 3.14159
    area = pi * (radius ** 2)
    return area
 
# 예제 사용법
circle_radius = 5.0
circle_area = calculate_circle_area(circle_radius)
print(f"반지름이 {circle_radius}인 원의 넓이는 {circle_area:.2f} 제곱단위입니다.")

이 예제에서 calculate_circle_area 함수는 radius 매개변수를 받아들이고 pi * (radius ** 2) 공식을 사용하여 넓이를 계산한 다음 결과를 반환합니다. 함수에는 또한 함수의 목적, 입력 매개변수 및 반환 값에 대한 간단한 설명을 제공하는 docstring이 포함되어 있습니다.

모듈과 패키지

파이썬의 모듈화된 디자인을 통해 코드를 재사용 가능한 구성요소로 구성할 수 있습니다. 모듈은 변수, 함수 및 클래스의 정의를 포함하는 파이썬 파일입니다. 모듈을 가져오면 해당 모듈이 제공하는 코드에 액세스하고 사용할 수 있습니다.

사용자 정의 모듈을 생성하고 사용하는 방법 예제입니다:

# my_module.py
def greet(name):
    print(f"안녕하세요, {name}님!")
 
# main.py
import my_module
 
my_module.greet("Alice")

이 예제에서는 greet 함수를 포함하는 my_module.py라는 모듈을 만듭니다. main.py 파일에서 my_module을 임포트하고 greet 함수를 호출합니다.

패키지는 관련된 모듈의 모음입니다. 이들은 코드를 구성하고 Python 코드를 그룹화하고 배포하는 방법을 제공합니다. 다음은 간단한 패키지 구조의 예입니다:

my_package/
    __init__.py
    module1.py
    module2.py
    subpackage/
        __init__.py
        module3.py

이 예에서 my_package는 두 개의 모듈(module1.pymodule2.py)과 하위 패키지(subpackage)를 포함하는 패키지입니다. 패키지 및 하위 패키지의 __init__.py 파일은 패키지의 구조와 동작을 정의하는 데 사용됩니다.

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

객체지향 프로그래밍 (OOP)은 데이터 (속성)와 해당 데이터를 나타내고 조작하는 함수 (메서드)를 모두 포함하는 개체를 생성하는 데 중점을 둔 프로그래밍 패러다임입니다. OOP는 클래스, 상속 및 다형성과 같은 개념을 제공하여 더 구조화되고 재사용 가능한 코드를 생성하는 데 도움을 줍니다.

파이썬에서 간단한 클래스의 예입니다:

class Dog:
    def __init__(self, name, breed):
        self.name = name
        self.breed = breed
 
    def bark(self):
        print("멍멍!")
 
# 예제 사용법
my_dog = Dog("Buddy", "Labrador")
print(my_dog.name)  # 출력: Buddy
print(my_dog.breed)  # 출력: Labrador
my_dog.bark()  # 출력: 멍멍!

이 예에서는 __init__ 메서드를 갖는 Dog 클래스를 정의합니다. __init__ 메서드는 namebreed 속성을 초기화합니다. 클래스에는 또한 "멍멍!"이라고 출력하는 bark 메서드가 있습니다. 그런 다음 Dog 클래스의 인스턴스를 생성하고 해당 속성과 메서드에 액세스합니다.

OOP는 또한 자식 클래스가 부모 클래스에서 속성과 메서드를 상속할 수 있는 상속과 같은 개념을 제공합니다. 다음은 예입니다:

class GuideDog(Dog):
    def __init__(self, name, breed, can_guide):
        super().__init__(name, breed)
        self.can_guide = can_guide
 
    def guide(self):
        print("주인을 안내할 수 있습니다.")
 
# 예제 사용법
my_guide_dog = GuideDog("Buddy", "Labrador", True)
print(my_guide_dog.name)  # 출력: Buddy
print(my_guide_dog.breed)  # 출력: Labrador
print(my_guide_dog.can_guide)  # 출력: True
my_guide_dog.bark()  # 출력: 멍멍!
my_guide_dog.guide()  # 출력: 주인을 안내할 수 있습니다.

이 예에서 GuideDog 클래스는 Dog 클래스를 상속하고 새로운 can_guide 속성과 guide 메서드를 추가합니다. GuideDog 클래스의 __init__ 메서드는 namebreed 속성을 초기화하기 위해 상위 Dog 클래스의 __init__ 메서드를 사용하는 super().__init__을 호출합니다.

예외와 오류 처리

예외는 프로그램의 실행 중에 발생하여 프로그램 명령의 정상적인 흐름을 방해하는 이벤트입니다. 파이썬에는 예외 처리 메커니즘이 내장되어 있어 이러한 예외를 예상하고 처리할 수 있습니다.

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 블록에서 ab로 나누려 합니다. ZeroDivisionError가 발생하면 except 블록의 코드가 실행되고 메시지가 출력됩니다. 함수는 나눗셈 작업이 성공하지 않았음을 나타내기 위해 None을 반환합니다.

또한 예외가 발생했든 발생하지 않았든 finally 블록을 사용하여 코드를 실행할 수도 있습니다. 이는 파일을 닫거나 데이터베이스 연결과 같은 리소스를 정리하는 데 유용합니다.

try:
    file = open("file.txt", "r")
    content = file.read()
    print(content)
except FileNotFoundError:
    print("오류: 파일을 찾을 수 없습니다.")
finally:
    file.close()

이 예에서 finally 블록은 try 블록에서 예외가 발생하든 발생하지 않든 파일이 닫히도록 보장합니다.

결론

이 파이썬 자습서에서는 함수, 모듈 및 패키지, 객체지향 프로그래밍, 예외 처리와 같은 다양한 주제를 다루었습니다. 이러한 개념은 효과적이고 유지 보수 가능한 파이썬 코드를 작성하는 데 기본이 됩니다. 이러한 기술을 이해하고 적용함으로써, 능숙한 Python 프로그래머가 되는 길에 본격적으로 나아갈 수 있습니다. 꾸준히 연습하고 다양한 코드 예제를 실험하며, Python 라이브러리와 프레임워크의 거대한 생태계를 탐색하여 지식과 기술을 확장해보세요.

코딩 즐겁게 하세요!

MoeNagy Dev