# 4. Pandas DataFrame 생성과 기초 조작

## 📚 학습 목표
- Pandas DataFrame의 기본 개념과 구조 이해
- 다양한 방법으로 DataFrame 생성하기
- DataFrame의 기본 조작과 속성 활용
- 컬럼과 행의 추가, 삭제, 수정 방법 습득

## 🔍 문제 설명

Pandas DataFrame은 2차원 라벨링된 데이터 구조로, 행과 열로 구성된 테이블 형태의 데이터를 효율적으로 다룰 수 있게 해줍니다. 데이터 분석의 핵심 도구이며, 실제 비즈니스 데이터를 다룰 때 가장 많이 사용됩니다.

### 실전 활용 사례
- **비즈니스 데이터**: 고객 정보, 판매 데이터, 재무 데이터
- **과학 데이터**: 실험 결과, 측정 데이터, 연구 데이터
- **웹 스크래핑**: 웹사이트에서 수집한 테이블 데이터
- **데이터베이스 연동**: SQL 쿼리 결과를 DataFrame으로 변환

## 📝 문제

### 문제 4-1: DataFrame 생성
1. 딕셔너리를 사용하여 직원 정보 DataFrame을 생성하세요.
2. 리스트의 리스트를 사용하여 DataFrame을 생성하세요.
3. NumPy 배열을 사용하여 DataFrame을 생성하세요.

### 문제 4-2: DataFrame 기본 조작
4. DataFrame의 기본 정보(shape, columns, index)를 확인하세요.
5. 특정 컬럼을 선택하고 새로운 DataFrame을 만드세요.
6. 특정 행을 선택하세요.

### 문제 4-3: DataFrame 수정
7. 새로운 컬럼을 추가하세요.
8. 기존 컬럼의 값을 수정하세요.
9. 특정 행을 삭제하세요.

## 💡 해답 및 설명

### 해답 4-1: DataFrame 생성

```python
import pandas as pd
import numpy as np

# 1. 딕셔너리를 사용하여 직원 정보 DataFrame 생성
employee_data = {
    '이름': ['김철수', '이영희', '박민수', '정수진', '최재영'],
    '나이': [28, 35, 42, 31, 27],
    '부서': ['개발', '인사', '개발', '마케팅', '개발'],
    '급여': [3500000, 4200000, 3800000, 3900000, 3600000],
    '입사일': ['2020-03-15', '2018-07-01', '2015-11-20', '2019-05-10', '2021-01-08']
}

df_employees = pd.DataFrame(employee_data)
print("직원 정보 DataFrame:")
print(df_employees)
print(f"\nDataFrame 형태: {df_employees.shape}")
print(f"컬럼명: {list(df_employees.columns)}")

# 2. 리스트의 리스트를 사용하여 DataFrame 생성
data_list = [
    ['노트북', 1200000, 50, '전자제품'],
    ['스마트폰', 800000, 100, '전자제품'],
    ['태블릿', 500000, 30, '전자제품'],
    ['이어폰', 150000, 200, '액세서리'],
    ['스마트워치', 300000, 80, '액세서리']
]

df_products = pd.DataFrame(data_list, 
                          columns=['제품명', '가격', '재고', '카테고리'])
print("\n제품 정보 DataFrame:")
print(df_products)

# 3. NumPy 배열을 사용하여 DataFrame 생성
np.random.seed(42)
random_data = np.random.randn(5, 4)  # 5행 4열의 랜덤 데이터
df_random = pd.DataFrame(random_data, 
                        columns=['A', 'B', 'C', 'D'],
                        index=['row1', 'row2', 'row3', 'row4', 'row5'])
print("\n랜덤 데이터 DataFrame:")
print(df_random)

# 추가 생성 방법들
print("\n다양한 DataFrame 생성 방법:")

# Series의 딕셔너리로 생성
series_dict = {
    '수학': pd.Series([85, 92, 78, 96, 88], index=['학생1', '학생2', '학생3', '학생4', '학생5']),
    '영어': pd.Series([90, 85, 92, 88, 95], index=['학생1', '학생2', '학생3', '학생4', '학생5']),
    '과학': pd.Series([88, 90, 85, 92, 87], index=['학생1', '학생2', '학생3', '학생4', '학생5'])
}

df_scores = pd.DataFrame(series_dict)
print("성적 DataFrame:")
print(df_scores)
```

### 해답 4-2: DataFrame 기본 조작

```python
# 4. DataFrame의 기본 정보 확인
print("DataFrame 기본 정보:")
print(f"형태 (행, 열): {df_employees.shape}")
print(f"컬럼명: {list(df_employees.columns)}")
print(f"인덱스: {list(df_employees.index)}")
print(f"데이터 타입:\n{df_employees.dtypes}")
print(f"메모리 사용량: {df_employees.memory_usage(deep=True).sum()} bytes")

# info() 메서드로 한 번에 확인
print("\nDataFrame 상세 정보:")
print(df_employees.info())

# describe() 메서드로 수치형 데이터 요약
print("\n수치형 데이터 통계:")
print(df_employees.describe())

# 5. 특정 컬럼 선택
print("\n컬럼 선택:")
# 단일 컬럼 선택 (Series 반환)
names = df_employees['이름']
print("이름 컬럼 (Series):")
print(names)

# 여러 컬럼 선택 (DataFrame 반환)
basic_info = df_employees[['이름', '나이', '부서']]
print("\n기본 정보 (DataFrame):")
print(basic_info)

# 6. 특정 행 선택
print("\n행 선택:")
# 인덱스로 선택
first_row = df_employees.iloc[0]
print("첫 번째 행:")
print(first_row)

# 라벨로 선택
second_row = df_employees.loc[1]
print("\n두 번째 행:")
print(second_row)

# 여러 행 선택
first_three_rows = df_employees.iloc[0:3]
print("\n처음 3행:")
print(first_three_rows)

# 조건부 행 선택
dev_employees = df_employees[df_employees['부서'] == '개발']
print("\n개발부 직원:")
print(dev_employees)
```

### 해답 4-3: DataFrame 수정

```python
# 7. 새로운 컬럼 추가
print("새로운 컬럼 추가:")

# 스칼라 값으로 컬럼 추가
df_employees['성별'] = '남성'
print("성별 컬럼 추가 후:")
print(df_employees)

# 계산된 값으로 컬럼 추가
df_employees['연봉'] = df_employees['급여'] * 12
print("\n연봉 컬럼 추가 후:")
print(df_employees)

# 조건부 값으로 컬럼 추가
df_employees['경력'] = df_employees['나이'] - 22
print("\n경력 컬럼 추가 후:")
print(df_employees)

# 8. 기존 컬럼 값 수정
print("\n컬럼 값 수정:")

# 특정 조건으로 값 수정
df_employees.loc[df_employees['부서'] == '개발', '성별'] = '남성'
df_employees.loc[df_employees['부서'] == '인사', '성별'] = '여성'
df_employees.loc[df_employees['부서'] == '마케팅', '성별'] = '여성'

print("성별 수정 후:")
print(df_employees)

# 스칼라 연산으로 값 수정
df_employees['급여'] = df_employees['급여'] * 1.1  # 10% 인상
print("\n급여 10% 인상 후:")
print(df_employees)

# 9. 특정 행 삭제
print("\n행 삭제:")

# 인덱스로 행 삭제
df_employees_dropped = df_employees.drop(2)  # 인덱스 2인 행 삭제
print("인덱스 2 행 삭제 후:")
print(df_employees_dropped)

# 조건부로 행 삭제
df_employees_filtered = df_employees[df_employees['나이'] < 40]
print("\n40세 미만 직원만:")
print(df_employees_filtered)

# 컬럼 삭제
df_employees_clean = df_employees.drop(['성별', '경력'], axis=1)
print("\n성별, 경력 컬럼 삭제 후:")
print(df_employees_clean)
```

## 🔧 주요 DataFrame 메서드와 속성

### 기본 속성
- `df.shape`: (행 수, 열 수) 튜플
- `df.columns`: 컬럼명 인덱스
- `df.index`: 행 인덱스
- `df.dtypes`: 각 컬럼의 데이터 타입
- `df.values`: NumPy 배열로 변환
- `df.size`: 전체 원소 개수

### 정보 확인 메서드
- `df.info()`: DataFrame 정보 요약
- `df.describe()`: 수치형 데이터 통계
- `df.head(n)`: 처음 n행
- `df.tail(n)`: 마지막 n행
- `df.isnull()`: NaN 값 확인
- `df.notnull()`: 비NaN 값 확인

### 선택 메서드
- `df.loc[]`: 라벨 기반 인덱싱
- `df.iloc[]`: 정수 위치 기반 인덱싱
- `df.at[]`: 단일 라벨 값
- `df.iat[]`: 단일 위치 값

### 수정 메서드
- `df.drop()`: 행/열 삭제
- `df.rename()`: 컬럼명/인덱스명 변경
- `df.set_index()`: 인덱스 설정
- `df.reset_index()`: 인덱스 초기화
- `df.sort_values()`: 값 기준 정렬
- `df.sort_index()`: 인덱스 기준 정렬

## 📊 추가 연습문제

### 연습문제 1: DataFrame 생성과 조작
```python
# 다음 문제들을 풀어보세요:
# 1. 학생들의 성적 데이터를 DataFrame으로 생성하세요.
#    (이름, 수학, 영어, 과학, 사회)
# 2. 각 학생의 평균 점수를 계산하여 새로운 컬럼을 추가하세요.
# 3. 평균 점수가 85점 이상인 학생들만 선택하세요.
```

### 연습문제 2: 데이터 필터링과 정렬
```python
# 다음 DataFrame을 생성하고 문제를 풀어보세요:
sales_data = pd.DataFrame({
    '제품명': ['노트북', '스마트폰', '태블릿', '이어폰', '스마트워치'],
    '가격': [1200000, 800000, 500000, 150000, 300000],
    '판매량': [50, 100, 30, 200, 80],
    '카테고리': ['전자제품', '전자제품', '전자제품', '액세서리', '액세서리']
})

# 1. 가격이 500,000원 이상인 제품만 선택하세요.
# 2. 판매량 기준으로 내림차순 정렬하세요.
# 3. 각 제품의 총 매출액(가격 × 판매량)을 계산하세요.
```

### 연습문제 3: 조건부 데이터 처리
```python
# 다음 DataFrame을 생성하고 문제를 풀어보세요:
customer_data = pd.DataFrame({
    '고객ID': [1, 2, 3, 4, 5, 6, 7, 8],
    '이름': ['김철수', '이영희', '박민수', '정수진', '최재영', '강지원', '윤미라', '손태식'],
    '나이': [28, 35, 42, 31, 27, 36, 29, 45],
    '등급': ['Gold', 'Silver', 'Gold', 'Platinum', 'Silver', 'Gold', 'Silver', 'Platinum'],
    '구매금액': [150000, 80000, 200000, 300000, 120000, 180000, 90000, 250000]
})

# 1. 나이가 30세 이상인 고객만 선택하세요.
# 2. Gold 등급 고객들의 평균 구매금액을 계산하세요.
# 3. 등급별 고객 수를 계산하세요.
```

## ⚠️ 주의사항 및 팁

1. **인덱싱 방법**: `loc`는 라벨 기반, `iloc`는 정수 위치 기반입니다.
2. **뷰 vs 복사**: 슬라이싱은 뷰를 반환하지만, 조건부 선택은 복사를 반환합니다.
3. **데이터 타입**: DataFrame은 각 컬럼마다 다른 데이터 타입을 가질 수 있습니다.
4. **메모리 효율성**: 큰 데이터셋에서는 적절한 데이터 타입을 선택하는 것이 중요합니다.
5. **인덱스 관리**: 인덱스는 고유해야 하며, 중복된 인덱스가 있으면 경고가 발생합니다.

## 🎯 실전 활용 예제

### 예제 1: 고객 데이터 분석
```python
# 고객 데이터 생성
np.random.seed(42)
n_customers = 100

customer_data = pd.DataFrame({
    '고객ID': range(1, n_customers + 1),
    '나이': np.random.randint(18, 70, n_customers),
    '성별': np.random.choice(['남성', '여성'], n_customers),
    '등급': np.random.choice(['Bronze', 'Silver', 'Gold', 'Platinum'], n_customers, p=[0.4, 0.3, 0.2, 0.1]),
    '연간구매액': np.random.normal(500000, 200000, n_customers),
    '가입일': pd.date_range('2020-01-01', periods=n_customers, freq='D')
})

print("고객 데이터 기본 정보:")
print(customer_data.info())

# 등급별 통계
grade_stats = customer_data.groupby('등급').agg({
    '나이': ['mean', 'std'],
    '연간구매액': ['mean', 'sum', 'count']
}).round(2)

print("\n등급별 통계:")
print(grade_stats)
```

### 예제 2: 판매 데이터 분석
```python
# 월별 판매 데이터 생성
np.random.seed(42)
months = pd.date_range('2023-01-01', periods=12, freq='M')
products = ['노트북', '스마트폰', '태블릿', '이어폰', '스마트워치']

sales_data = []
for month in months:
    for product in products:
        sales_data.append({
            '월': month,
            '제품': product,
            '판매량': np.random.randint(10, 100),
            '단가': np.random.choice([100000, 150000, 200000, 50000, 80000])
        })

df_sales = pd.DataFrame(sales_data)
df_sales['매출액'] = df_sales['판매량'] * df_sales['단가']

print("판매 데이터:")
print(df_sales.head(10))

# 월별 총 매출액
monthly_sales = df_sales.groupby('월')['매출액'].sum()
print("\n월별 총 매출액:")
print(monthly_sales)

# 제품별 평균 판매량
product_avg = df_sales.groupby('제품')['판매량'].mean()
print("\n제품별 평균 판매량:")
print(product_avg)
```

### 예제 3: 주식 데이터 분석
```python
# 주식 가격 데이터 생성
np.random.seed(42)
dates = pd.date_range('2023-01-01', periods=100, freq='D')
stocks = ['삼성전자', 'SK하이닉스', 'LG에너지솔루션', '현대차', '기아']

stock_data = []
for date in dates:
    for stock in stocks:
        stock_data.append({
            '날짜': date,
            '종목': stock,
            '시가': np.random.normal(50000, 10000),
            '고가': np.random.normal(52000, 10000),
            '저가': np.random.normal(48000, 10000),
            '종가': np.random.normal(50000, 10000),
            '거래량': np.random.randint(1000000, 10000000)
        })

df_stocks = pd.DataFrame(stock_data)

# 일별 수익률 계산
df_stocks['수익률'] = df_stocks.groupby('종목')['종가'].pct_change()

print("주식 데이터:")
print(df_stocks.head(10))

# 종목별 평균 수익률
stock_returns = df_stocks.groupby('종목')['수익률'].agg(['mean', 'std']).round(4)
print("\n종목별 수익률 통계:")
print(stock_returns)
```

## 📚 다음 단계

이제 Pandas DataFrame의 기본을 익혔습니다. 다음 단계에서는:
- 데이터 인덱싱, 슬라이싱, 필터링
- 결측치 처리와 데이터 정제
- 그룹화와 집계
- 데이터 병합과 조인

을 학습하게 됩니다.

---

**참고 자료:**
- [Pandas DataFrame 공식 문서](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.html)
- [Pandas DataFrame 튜토리얼](https://pandas.pydata.org/docs/user_guide/dsintro.html#dataframe) 