Python 프로그래밍 및 Pandas 활용 실습(4)

2024. 1. 5. 19:20TIL

DataFrame심화 - 데이터셋 확인하기

 데이터셋: 고객 성격 분석 : https://www.kaggle.com/datasets/imakash3011/customer-personality-analysis

 

Customer Personality Analysis

Analysis of company's ideal customers

www.kaggle.com

import pandas as pd
customers = pd.read_csv('marketing_campaign.csv') #구분자가 쉼표일때
customers = pd.read_csv('marketing_campaign.csv', sep='\t') #구분자가 탭키일때
customers = pd.read_csv('marketing_campaign.csv', sep='\t', index_col='ID') #인덱스를 ID로 설정

# 데이터프레임 정보
customers.info()
# 집계통계 기술
customers.describe().round()
# 집계통계 기술 - 숫자 아닌 정보 확인
customers.describe(exclude=['int','float'])

 

DataFrame심화 - DataFrame과 Series 각 데이터 함수적용(apply)
import pandas as pd
customers = pd.read_csv('marketing_campaign.csv', sep='\t', index_col='ID')

# 나이 계산 - 데이터셋이 2021.08.23임
2021 - customers['Year_Birth']
# 연도 옆에 추가
customers.insert(1, 'Age', 2021 - customers['Year_Birth'])

#.apply(func)
# 연령대 분류
def classify_age(age):
    if age >= 0 and age <= 12:
        return 'Child'
    elif age >= 13 and age <= 19:
        return 'Teenager'
    elif age >= 20 and age <= 39:
        return 'Young Adult'
    elif age >= 40 and age <= 64:
        return 'Middle-aged'
    elif age >= 65:
        return 'Elderly'
    else:
        return 'Invalid age'
customers['Age_group'] = customers['Age'].apply(classify_age)
# 열 이동 = 제거&반환 - 새로 삽입
data = customers.pop('Age_group')
customers.insert(2, 'Age_Group', data)
# 연령 분류 확인
customers.value_counts('Age_Group')

#.apply(func)의 여러 열 데이터 활용하기
# 육류, 어류 지출 금액 합산
def total_meat_fish(row):
    meat = row['MntMeatProducts']
    fish = row['MntFishProducts']
    return meat+fish #sum(meat,fish)
customers.apply(total_meat_fish, axis=1) #DataFrame에서 apply할 땐 axis 고려
customers.insert(6, 'Meat+Fish', customers.apply(total_meat_fish, axis=1))

#.apply(lambda_func)
# 연간 수익을 달러에서 원화로 변경
# 2023-12-09 기준 1달러 == 1312원
income_kr = customers['Income'].apply(lambda x: x * 1312)
customers.insert(6, 'Income_kr', income_kr)

 

DataFrame심화 - Series값 매핑(map)과 DataFrame요소 함수적용(applymap)
people_set = {
    'Name' : ['Spencer', 'Mark', 'Tom', 'Peter'],
    'Major': ['Computer', 'Science', 'English', 'Computer'],
    'YearOfJoining' : [2020, 2019, 2018, 2017],
    'DriverLicense' : [True, False, False, True],
    'TeacherCertification' : [True, False, False, False],
}
import pandas as pd
people = pd.DataFrame(people_set)

# map() 으로 매핑 확인해서 값 변환
people['DriverLicense']= people['DriverLicense'].map({True: 'Yes', False: 'No'})
# 매핑이 안된 값은 NA
people['TeacherCertification'].map({True: 'ComputerScience'})
# 매핑하여 개수 확인
people['TeacherCertification'].map({True: 'ComputerScience',False: 'NoCertification'}).value_counts()

#Dataframe.applymap(func)
people.applymap(str).applymap(type)
# str같은 경우 Series는 applymap할 필요 없이 가능하나
people['Name'].str.len()
# Dataframe은 .str이 안된다.
people.str #error
# 모든 요소를 str로 변환 후 대문자 변환
# 대문자 만드는 함수
def uppercase(data):
    return data.upper()
people.applymap(str).applymap(uppercase)
people.astype(str).applymap(uppercase) # 형변환 함수 사용


# Lambda도 살펴보자
df = pd.DataFrame({'A': [1, 2, 3],
                   'B': [4, 5, 6],
                   'C': [7, 8, 9]})
# applymap()을 사용하여 제곱 연산 적용
df_squared = df.applymap(lambda x: x**2)
df**2 #동일

import pandas as pd
customers = pd.read_csv('marketing_campaign.csv', sep='\t', index_col='ID')
# 다자녀 표시
customers['Kidhome'] = customers['Kidhome'].map({0:'자녀없음',1:'외동',2:'다자녀'})
# 모두 대문자로 변환
customers.astype(str).applymap(uppercase)

 

DataFrame심화 - 타입 변환(astype)
import pandas as pd
customers = pd.read_csv('marketing_campaign.csv', sep='\t', index_col='ID')

# .info()로 dtype과 메모리 확인 
customers.info()
# dtypes: float64(1), int64(24), object(3)
# memory usage: 507.5+ KB

'''
.astype(dtype)로 형변환
-NA는 형변환이 불가
-이번 실습으로 데이터프레임의 메모리를 줄여보자. 이를 위해 int64를 int8로 줄일 예정
-int64는 64비트를 표현 가능한 범위를 사용
-int8은 8비트로 표현 가능한 범위를 사용
'''
# int64 -> int16 탄생연도
customers['Year_Birth'] = customers['Year_Birth'].astype('uint16')
customers.info() 
# dtypes: float64(1), int64(23), object(3), uint16(1) 
# memory usage: 494.4+ KB

# int64 -> int8 자녀 수, 청소년 수
customers['Kidhome'] = customers['Kidhome'].astype('int8')
customers['Teenhome'] = customers['Teenhome'].astype('int8')
customers.info() 
# dtypes: float64(1), int64(21), int8(2), object(3), uint16(1)
# memory usage: 463.8+ KB

# int64 -> bool 자녀 수, 청소년 수
customers['Kidhome'] = customers['Kidhome'].astype('bool')
customers['Teenhome'] = customers['Teenhome'].astype('bool')
customers.info()
# dtypes: bool(2), float64(1), int64(21), object(3), uint16(1)
# memory usage: 463.8+ KB

 

DataFrame심화 - 값 교체(replace)
import pandas as pd
customers = pd.read_csv('marketing_campaign.csv', sep='\t', index_col='ID')

# 결혼 여부의 Alone, Absurd, YOLO를 Single로 귀속
# 방법 1. 시리즈.replace(dict)
customers['Marital_Status'].replace({'Alone':'Single','Absurd':'Single','YOLO':'Single'})

# 방법 2. 시리즈.replace(to_replace:list, value)
customers['Marital_Status'].replace(['Alone','Absurd','YOLO'],'Single')

# 방법 3. 데이터프레임.replace()
customers.replace({'Alone':'Single','Absurd':'Single','YOLO':'Single'})
# Inplcae=True로 원본에 반영하기
customers.replace({'Alone':'Single','Absurd':'Single','YOLO':'Single'},inplace=True)

#없는 값을 바꾸려고 하면?
customers.replace('홍길동',0000000) #아무 에러도 발생하지 않는다.

 

DataFrame심화 - 조건식(where)
import pandas as pd
customers = pd.read_csv('marketing_campaign.csv', sep='\t', index_col='ID')

# 복습 - 다자녀(2인 이상)을 확인
multiple_child_mask = customers['Kidhome'] >= 2
customers[multiple_child_mask].head()

# .where()를 사용할 때 조건에 맞는 데이터가 아니면 NA를 반환한다.
customers.where(customers['Kidhome'] >= 2)
# .where()이후 .dropna()
customers.where(customers['Kidhome'] >= 2).dropna()

#조건에 맞지않는 데이터는 은닉하기
# 방법 1. other로 대체하기
customers.where(customers['Kidhome'] >= 2, other='---')
# 방법 2. default인 NA가 되도록 놓거나 other에 None으로 대체하기
customers.where(customers['Kidhome'] >= 2, other=None)

 

DataFrame심화 - 여러 집계함수 동시 적용(agg)
import pandas as pd
customers = pd.read_csv('marketing_campaign.csv', sep='\t', index_col='ID')

'''
.agg():지정된 축(axis)을 기준으로 하나 이상의 연산을 사용하여 데이터를 집계(aggregate) 하는 기능을 제공

parameter
-func : function, str, list or dict
	데이터를 집계하는 데 사용할 함수를 지정.
	함수로는 DataFrame을 인자로 받을 수 있는 함수나 
    DataFrame의 apply() 메소드에 사용할 수 있는 함수여야 한다.
	허용 가능한 조합
		-함수
		-함수 이름의 문자열
		-함수나 함수 이름이 포함된 리스트
		-축 레이블과 함수 또는 함수 이름이 포함된 딕셔너리 형태의 조합
-axis : {0 or ‘index}
	데이터를 집계할 축을 지정.
	0 또는 'index'인 경우 각 열(column)을 기준으로 함수를 적용.
	1 또는 'columns'인 경우 각 행(row)을 기준으로 함수를 적용.
-*args와 **kwargs: 함수에 전달할 위치 인자와 키워드 인자를 지정할 수 있다.

return
-Scalar: Series.agg() 메소드가 단일 함수로 호출될 때 반환됩니다.
-Series: DataFrame.agg() 메소드가 단일 함수로 호출될 때 반환됩니다.
-DataFrame: DataFrame.agg() 메소드가 여러 함수로 호출될 때 반환됩니다.
'''
# 방법1. dict로 전달
# Income -> max, Kidhome -> mean
customers.agg({'Income': 'max', 'Kidhome': 'mean'})
# Income -> [min, max, std], Kidhome -> ['min', 'max', 'mean']
customers.agg({'Income': ['min', 'max', 'std'], 'Kidhome': ['min', 'max', 'mean']})

# 방법2. dict없이 str 또는 list로 전달하기
customers.agg('max') #Series
# max, min
customers.agg(['max','min']) #DataFrame

# max, min, mean
customers.agg(['max','min','mean']) #Error
#customers 데이터프레임의 ['Education', 'Marital_Status', 'Dt_Customer'] 열에 숫자형이 아니기 때문
# 대처 1 : drop() agg() 체인형으로 해결
customers.drop(columns=['Education', 'Marital_Status', 'Dt_Customer']).agg(['max','min','mean'])
# 대처 2 : 새로운 df를 만들어 해결
customers_numeric = customers.select_dtypes(include='number') #숫자형 열만 선택
customers_numeric.agg(['max','min','mean'])
# 대처 3 : 원본에서 제거. 대처1을 파괴적으로 처리하는 방법
customers.drop(columns=['Education', 'Marital_Status', 'Dt_Customer'],inplace=True)
customers.agg(['max','min','mean'])

# 주의 : agg()는 numeric_only=True가 안된다.
customers.agg(['max', 'min', 'mean'], numeric_only=True)

 

DataFrame심화 - 데이터프레임 복제와 일치확인(copy, equals)
import pandas as pd
customers = pd.read_csv('marketing_campaign.csv', sep='\t', index_col='ID')

#copy() - 원본과 분리해서 작업하고 싶을 때 사용
# 복제 방법
customers_copy = customers.copy()
# 주소 비교
id(customers), id(customers_copy) # 다름
customers is customers_copy # False
# 값 비교 1
customers == customers_copy # 각 값이 모두 True
# 값 비교 2
customers.equals(customers_copy) # True

# 결혼 상태 Series
marital_status = customers['Marital_Status']
# 값을 변경하면 에러가 발생한다.
marital_status[0:3] = '000'
# 원본의 데이터가 변경되어있다.
customers.head()

#원래 의도는 별도의 데이터프레임을 만들고 작업하려고 했던 거라면
# .copy()
marital_status = customers['Marital_Status'].copy()
marital_status[0:3] = '000'
# 원본은 변경되지 않음
customers.head()

 

DataFrame심화 - 멀티인덱싱(Multi-indexing, set_index, get_level_values, swaplevel)
import pandas as pd
customers = pd.read_csv('marketing_campaign.csv', sep='\t')
'''
멀티인덱싱
-하나 이상의 인덱스 레벨을 가지는 인덱스 구조.
-데이터프레임이나 시리즈의 다차원적인 인덱싱을 지원하기 위해 사용.
-멀티인덱스로 인해 데이터를 계층적(hierarchical)으로 조직화.
-다양한 차원(Multi-level)에서 데이터에 접근 가능.
-멀티인덱스 생성은 데이터프레임 또는 시리즈를 생성할 때 인덱스를 array같은 형태로 지정한다.
'''
# 멀티인덱스 설정 방법 1
customers = pd.read_csv('marketing_campaign.csv', sep='\t', index_col=['ID','Marital_Status'])
# set_index()로 인한 멀티인덱스 설정
customers.set_index(['ID','Marital_Status'], inplace=True)

# .get_level_values(level)
customers.index.get_level_values(0) # 첫번째 인덱스의 값들
customers.index.get_level_values(1) # 두번째 인덱스의 값들

# .swaplevel()
customers.swaplevel() # 비파괴적
customers = customers.swaplevel() # 인덱스 순서 변경
# index가 섞여있으니 정렬
customers.sort_index()
# 아니면 index열을 지정하여 정렬
customers.sort_values('Marital_Status')

# 멀티인덱스를 활용해 조회하기
# Select row - index level 0
customers.loc['Single'] # DataFrame
# Select row - columns
customers.loc['Single','Income'] # Series
customers.loc[('Single',5524),'Income'] # 스칼라
customers.loc[('Single',5524)] # Single,5524의 값들
customers.loc[('Single',5524),('Income', 'Kidhome')] # Single,5524의 Income,Kidhome 값

# 슬라이싱(:)을 이용한 인덱스 여러개 값 조회
# 'Single', 'Alone', 'Absurd', 'YOLO' 만 조회
customers.loc[['Single', 'Alone', 'Absurd', 'YOLO']]
customers.loc[['Single', 'Alone', 'Absurd', 'YOLO'],:]
# 2개의 인덱스를 사용할 땐 .loc[([lv1_labels], [lv2_labels])]
customers.loc[(['Single', 'Alone', 'Absurd', 'YOLO'], [5524, 2114, 92, 4369, 11133]),]
customers.loc[(['Single', 'Alone', 'Absurd', 'YOLO'], [5524, 2114, 92, 4369, 11133]),:]

# 특정 level 생략 - 행 조회
# 특정 row 조회
customers.loc[:,2114,:]
# 특정 여러 row 조회
customers.loc[:,[2114,92],:]
# 뒤에 :를 생략하면 에러
customers.loc[:,[2114,92],]

# 특정 level 생략 - 행과 열 조회
# 하나씩 해도 에러
customers.loc[:,2114,'Income']
# 특정 row - col 조회
customers.loc[:,[2114,92],'Income'] # error
# 위에서 본 특정 여러 row 조회에서 조금 변경해보면? Try 1
customers.loc[(:,[2114,92]),'Income'] # error
# 위에서 본 특정 여러 row 조회에서 조금 변경해보면? Try 2
customers.loc[(:,[2114,92]),('Income','Kidhome')] #error
# 해결 방법 1
customers.loc[(slice(None),2114),'Income']
customers.loc[(slice(None),2114),['Income','Kidhome']]
# 해결 방법 2
customers.loc[pd.IndexSlice[:,2114],'Income']
customers.loc[pd.IndexSlice[:,2114],['Income','Kidhome']]

 

공부하며 어려웠던 내용

 코드를 작성하며 내가 생각한 결과와 다르게 나온 이유를 아직 명확하게 알지 못했다.