Python 프로그래밍 및 Pandas 활용 실습(5)-1
2024. 1. 6. 17:29ㆍTIL
문자열 제어 - .str의 메소드(upper, lower,)
import pandas as pd
pokemons = pd.read_csv('Pokemon.csv', index_col='#')
'''
.str
-문자열 메소드를 실행할 때는, .str이 먼저 나온다고 생각
-Series와 Index에 대한 벡터화된 문자열 함수
-반복문을 사용하지 않고도 간단하게 문자열 데이터를 처리할 수 있다. (문자열 검색, 치환, 분리, 결합 등)
-특정한 메서드에서 다르게 처리하지 않는 한 NA 값은 NA 상태로 유지
'''
pokemons['Name'].str
pokemons['Type 1'].str
# .str은 형변환이 아니다. 형변환은 .astype() 으로 하자
pokemons['Total'].astype['str'].str #error
# 시리즈가 string(object)여도 문자열 메소드는 바로 동작하지 않는다.
# 시리즈 객체에 upper() 메소드가 없다.
pokemons['Name'].upper()
# 형변환을 시켜줘도 메소드는 바로 동작하지 않는다.
pokemons['Name'].astype(str).upper()
# .str.upper()로 해야한다.
pokemons['Name'].str.upper()
# .str.lower()
pokemons['Name'].str.lower()
문자열 제어 - .str의 메소드(slicing)
# Name의 5자만 본다면?
# pokemons['Name'][0:5] # 이 코드는 첫번째부터 다섯번째 행까지 이름이 나옴
pokemons['Name'].str[0:5]
# 역정렬
pokemons['Name'].str[::-1] # 이름이 거꾸로 나옴
문자열 제어 - .str의 메소드(contains)
'''
.str.contains(pat)
Series.str.contains(pat, case=True, flags=0, na=None, regex=True)
-문자열 Series 또는 Index에서 주어진 패턴 또는 정규식이 포함되어 있는지 확인
-주어진 문자열이나 정규식이 다수의 문자열 요소에 대해 포함 여부를 효과적으로 확인
parameter:
-pat: 문자열 또는 정규식 패턴
-case : 대소문자 구분 여부를 지정하는 부울 값.
True면 대소문자를 구분.
-flags : re 모듈에 전달할 플래그. 정규식 패턴의 세부 제어 설정.
re.IGNORECASE(re.I) : 대소문자를 구분하지 않고 패턴을 매칭.
re.MULTILINE(re.M) : 문자열이 여러 줄로 이루어져 있을 때, 각 줄의 시작과 끝을 패턴 매칭.
re.DOTALL(re.S) : 줄바꿈 문자를 포함한 모든 문자와 매칭.
re.UNICODE(re.U) : 유니코드 문자 범위를 이해하고 매칭.
re.VERBOSE(re.X) : 정규식 패턴을 가독성 있게 작성할 수 있도록 여러 줄과 주석을 사용한다.
-na : 누락된 값에 대한 대체값(채움값). NA데이터를 만낫을 때 어떻게 처리할 건지 설정.
-regex: 패턴을 정규식인지를 파악할 지 설정.
True면 패턴을 정규식으로 해석.
False면 패턴을 리터럴 문자열로 이해하고 처리.
return
-포함 여부에 따라 부울 Series 또는 Index를 반환.
-패턴이나 정규식이 포함되어 있는 경우 True로 표시.
-포함되어 있지 않은 경우 False로 표시.
'''
# q 탐색 필터(mask랑 비슷), 대소문자 구분O
q_filter = pokemons['Name'].str.contains('q',case=True)
pokemons[q_filter]
# q 탐색 필터(mask랑 비슷), 대소문자 구분X
q_filter = pokemons['Name'].str.contains('q',case=False)
pokemons[q_filter]
# saurus(파충류), Dragon(용) -> saur, ragon 이름에 들어간 걸 찾기
o_filter = pokemons['Name'].str.contains('saur|ragon',case=False)
pokemons[o_filter]
문자열 제어 - .str의 메소드(startswith, endswith)
#.str.startswith()
# 1가지
start_filter1 = pokemons['Name'].str.startswith('Q')
pokemons[start_filter1]
# 2가지
start_filter2 = pokemons['Name'].str.startswith(('Q','Pid'))
pokemons[start_filter2]
#.str.endswith()
# 1가지
end_filter1 = pokemons['Name'].str.endswith('tle')
pokemons[end_filter1]
# 2가지
end_filter2 = pokemons['Name'].str.endswith(('tle','gon'))
pokemons[end_filter2]
문자열 제어 - .str의 메소드(replace)
# 타입 1이 Fire인 포켓몬 찾기
fire_filter = pokemons['Type 1'].str.contains('Fire')
pokemons[fire_filter].head()
# 'Fire'를 'Flame'로 변경하기
pokemons_copy = pokemons.copy()
pokemons_copy['Type 1'] = fire_filter = pokemons['Type 1'].str.replace('Fire','Flame')
flame_filter = pokemons_copy['Type 1'].str.contains('Flame')
pokemons_copy[flame_filter].head()
문자열 제어 - .str의 속성(index, columns)
# 실습을 위해서 Name을 ID로 설정
pokemons.set_index('Name',inplace=True)
# .index.str
pokemons.index.str[:5]
# columns.str
pokemons.columns.str.upper()
DataFrame재구성 - 행렬 전치(transpose)
import pandas as pd
# sample1.csv - 학업성취도
students = pd.read_csv('sample1.csv',index_col='학생번호')
# sample2.csv - 연도별사원성과
company = pd.read_csv('sample2.csv',index_col='사원번호')
# sample3.csv - 역대한국야구순위
league = pd.read_csv('sample3.csv',index_col='팀이름')
'''
.transpose()
-전치행렬 : 행과 열을 교환하여 얻는 행렬. 주대각선을 축으로 반사 대칭을 한다.
'''
# 전치를 해보면
students.transpose()
# 줄여서 .T
students.T
company.T
# 위의 전치는 유의미하지 않다. 데이터의 증가는 행의 방향이 옳다고 생각될 경우에 유의미
league.T # 유의미
# 주의사항 : 원본 != 전치*2
# 원본
company.info()
company.head()
# 전치 1회
t1_company = company.T
t1_company.info()
t1_company.head()
# 전치 2회
t2_company = t1_company.T
t2_company.info()
t2_company.head()
# 데이터 값은 같으나 dtype이 object로 변경
DataFrame재구성 - 인덱스 레벨 제어(stack, unstack, droplevel)
import pandas as pd
# sample1.csv - 학업성취도
students = pd.read_csv('sample1.csv', index_col='학생번호')
# sample2.csv - 연도별사원성과
company = pd.read_csv('sample2.csv', index_col='사원번호')
# sample3.csv - 역대한국야구순위
league = pd.read_csv('sample3.csv', index_col='팀이름')
# pokemon
pokemons = pd.read_csv('Pokemon.csv', index_col='Name')
# richest
richest = pd.read_csv('TopRichestInWorld.csv', index_col='Name')
# richest.drop(columns=['Source'], inplace=True)
'''
.stack()
-데이터프레임의 구조를 재조정하는 데 유용
-columns에 다중 인덱스가 있는 데이터프레임에서 사용하면 컬럼 인덱스가 로우 인덱스의 레벨로 이동.
-컬럼을 로우로 "압축"하는 작업을 수행
DataFrame.stack(level=-1, dropna=_NoDefault.no_default,
sort=_NoDefault.no_default, future_stack=False)
-컬럼에서 지정된 레벨(들)을 인덱스로 스택(stack)한다.
-현재의 데이터프레임과 비교하여 하나 이상의 새로운 내부 레벨을 가지는
다중 레벨 인덱스를 가진 재구성된 데이터프레임 또는 시리즈를 반환합니다.
새로운 내부 레벨은 현재 데이터프레임의 컬럼을 피벗(pivot)하여 생성된다.
만약 컬럼이 단일 레벨이라면, 결과는 시리즈(Series).
만약 컬럼이 다중 레벨이라면, 새로운 인덱스 레벨은 지정된 레벨에서 가져오며
결과는 데이터프레임(DataFrame).
Parameters
level : int, str, list, default -1
-컬럼 축에서 인덱스 축으로 스택(stack)할 레벨 지정.
-하나의 인덱스 또는 레이블, 또는 인덱스 또는 레이블의 리스트로 정의
dropna : bool, default True
-결과 프레임/시리즈에서 결측값을 가진 행을 삭제할지 여부.
-컬럼 레벨을 인덱스 축에 스택(stack)하면 원본 데이터프레임에 없는 인덱스와 컬럼 값의 조합이 생성될 수 있다.
sort : bool, default True
-결과 MultiIndex의 레벨을 정렬할지 여부.
future_stack : bool, default False
-현재 구현체를 대체할 새로운 구현체를 사용할지 여부.
-True인 경우, dropna와 sort는 결과에 영향을 미치지 않으며 지정하지 않아야 한다.
Returns : DataFrame or Series
스택(stack)된 데이터프레임 또는 시리즈.
'''
# 다중 레벨 컬럼을 가진 데이터프레임 생성
data = {
('A', 'col1'): [1, 5],
('A', 'col2'): [2, 6],
('B', 'col1'): [3, 7],
('B', 'col2'): [4, 8]
}
df = pd.DataFrame(data)
# index 0행 영역에서 columns -> index로 재조정
# index 1행 영역에서 columns -> index로 재조정
# unstack()은 되돌아갑니다.
# 1. 원본 -> stack()
stacked_df = df.stack()
# 2. 원본 -> stack() -> unstack()
unstacked_df = stacked_df.unstack() # 원본
# 3. 원본 -> stack() -> stack()
stacked_stacked_df = stacked_df.stack() # 시리즈
# 4. 원본 -> unstack()
unstacked_df = df.unstack() # 시리즈
# 단일 레벨의 Columns을 stack()
richest.stack()
richest = pd.DataFrame(richest.stack()) # 컬럼 이름 : 0
richest.columns # 컬럼이 없다
richest = richest.unstack() # 원본에서 컬럼 이름 : 0이 추가
richest = richest.droplevel(level=0, axis=1) # droplevel로 컬럼 이름 : 0을 삭제
# level값에 따른 stack, unstack변화
richest = pd.read_csv('TopRichestInWorld.csv', index_col=['Industry', 'Name'])
# 기본 unstack(level=-1)
richest_industry = richest.unstack()
# 지정 unstack(level=0)
richest_name = richest.unstack(level=0)
# 기존 열은 level 0
# unstack()으로 지정된 index는 level 1
# 교차가 되지 않는 값은 NA로 지정된다.
# 특정 산업에 대한 정보만 확인
richest_industry.loc['Automotive'].dropna()
pd.DataFrame(richest_industry.loc['Automotive'].dropna())
# 물론 이렇게 특정 분야의 데이터를 확인할 수 있다.
richest.loc[['Automotive']]
# 이를 stack()하면 조금 차이가 있다.
richest.loc[['Automotive']].unstack().T
DataFrame재구성 - 재구조화(melt)
'''
melt()
-넓은 형식으로 구성된 데이터프레임을 긴 형식으로 변환하여 데이터를 재구성
-함수를 사용하면 하나 이상의 열을 식별자 변수(id_vars)로 설정하고,
나머지 열인 측정 변수(value_vars)를 행 방향으로 언피벗하여
두 개의 비-식별자 열인 variable과 value만 남게 되는 형태로 데이터프레임을 변환
Unpivot
데이터프레임의 구조를 변형하여 wide(넓은) 형식에서 long(긴) 형식으로 데이터를 재구성하는 작업
일반적으로 wide(넓은) 형식의 데이터 프레임은
-각 행이 고유한 식별자를 가지고 있고,
-각 열은 실제 관측값이 있는 열로 구성.
반대로 long 형식의 데이터프레임은
-각 행이 하나의 관측값을 가지고 있고
-식별자 변수와 측정 변수를 나타내는 열로 구성
그래서 Unpivot 작업이란
-데이터프레임에서 측정 변수를 열에서 행 방향으로 이동시켜 long 형식으로 변환하는 것을 의미
-이를 통해 데이터의 구조를 변경, 분석, 시각화 작업에 더 적합한 형태로 데이터를 재구성
'''
# 예시 데이터 생성
data = {
'이름': ['Spencer', 'Lune', 'Mark'],
'국어': [66, 90, 95],
'영어': [77, 80, 65],
'수학': [99, 80, 75]
}
df = pd.DataFrame(data)
# wide 형식 데이터프레임 출력
df
# long형식 데이터프레임 출력
df.melt(id_vars='이름', value_vars=['국어','영어','수학'], var_name='과목', value_name='점수')
'''
melt() 함수를 사용하여 wide 형식의 데이터프레임을 long 형식으로 변환
1. id_vars 매개변수에 '이름'을 지정하여 식별자 변수로 설정
2. value_vars 매개변수에 ['국어', '영어', '수학']를 지정하여 언피벗할 열을 선택
3. var_name 매개변수에 '과목'을, value_name 매개변수에 '점수'를 지정하여 열 이름을 변경
-변환된 long 형식의 데이터프레임은 각 행이 하나의 관측값을 가지며, 이름, 과목, 점수 열로 구성
-보다 유연하게 데이터를 분석하거나 시각화하는 등의 작업을 수행할 수 있다.
'''
# sample3.csv - 역대한국야구순위
league = pd.read_csv('sample3.csv')
league.melt(id_vars='팀이름', value_vars=['2017','2018','2019','2020','2021'],
var_name='연도', value_name='순위')
# 코드 정리
cols = league.columns.drop('팀이름')
league.melt(id_vars='팀이름', value_vars=cols, var_name='연도', value_name='순위')
DataFrame재구성 - 피봇과 피봇테이블(pivot, pivot_table)
import pandas as pd
# sample2.csv - 연도별사원성과
company = pd.read_csv('sample2.csv')
# pivot() 실습
# 사원이름별 연도별 매출
company.pivot(index='연도',columns='사원이름',values='연도별매출')
# 연도별 사원이름별 매출
company.pivot(index='사원이름',columns='연도',values='연도별매출')
# values를 생략하면 모든 열이 사용됨
company.pivot(index='사원이름',columns='연도')
# 만일 index를 그대로 쓰고 싶다면
company.set_index('사원이름').pivot(columns='연도')
'''
.pivot() <-> .melt()
-2개의 메소드는 서로 다른 기능을 합니다.
-pivot() -> 피벗, Long 데이터프레임을 Wide으로
-melt() -> 언피벗, Wide 데이터프레임을 Long으로
'''
company_pivot = company.pivot(index='연도',columns='사원이름',values='연도별매출')
# .melt()는 .pivot()과 반대 기능을 한다
company_pivot.reset_index().melt(id_vars='연도',value_name='연도별매출')
'''
pivot_table()
매개변수들을 활용하여 Excel 스타일의 피벗 테이블을 생성하고, 이를 DataFrame으로 반환
'''
# 사원별 누적매출 통계내기
company.pivot_table(values='연도별매출',columns='사원이름',aggfunc='sum')
# 연도별 사원들의 평균매출 통계내기, aggfunc이 생략되면 mean
company.pivot_table(values='연도별매출',columns='연도')
# 소속팀별 연도별매출 평균을 확인하기
company.pivot_table(values='연도별매출',columns='소속팀')
# 소속팀별 연도별매출 평균을 연도별로 확인하기
company.pivot_table(values='연도별매출',columns='소속팀',aggfunc='mean',index='연도')
'''
pivot_table 인자가 헷갈리면
"A별 BB의 CCC함수값을 DDDD인덱스로 확인하기"
A별 -> columns
BB의 -> values
CCC함수값을 -> aggfunc
DDDD인덱스로 -> index
'''
DataFrame재구성 - 그룹화(groupby) 생성방법과 단일 열 그룹화
'''
.groupby()
-데이터를 그룹화하고 그룹 단위로 연산을 수행하는 기능을 제공하는 함수
-기준을 정하여 그룹으로 나누고, 각 그룹 단위로 연산을 수행.
진행 순서
1.그룹으로 나눌 열 또는 열들을 선택
-groupby() 함수에 그룹으로 나눌 열(label or list-like)을 전달.
-예로 df.groupby('업종')은 '업종'열을 기준으로 데이터를 그룹화.
2.그룹 객체에 원하는 연산을 수행
-그룹 객체를 생성되면 그룹 단위로 집계 함수로 연산을 수행.
-예로 df.groupby('업종').mean()은 '업종'열을 기준으로 그룹화된 데이터 평균을 계산.
'''
import pandas as pd
# 학생 성적 데이터 생성
data = {
'학생명': ['철수', '영희', '민수', '수현', '지영', '동희'],
'과목': ['수학', '영어', '수학', '과학', '영어', '과학'],
'성적': [90, 85, 92, 88, 95, 91]
}
df = pd.DataFrame(data)
# 과목별 평균 성적 계산
subject_mean = df.groupby('과목')['성적'].mean()
# 실습 1 - 단일 열 그룹화
# Pokemon.csv - 포켓몬
pokemons = pd.read_csv('Pokemon.csv', index_col='#')
# Type 1의 목록
pokemons.value_counts('Type 1')
type1_group = pokemons.groupby(by='Type 1')
# group화 된 상태 보기
type1_group.groups['Bug'] # {key : index객체}
# .get_group(value) -> DataFrame으로 확인
# mask로 조건식 하는 것과 같은 출력 pokemons['Type 1'] == 'Dark'
type1_group.get_group('Dark')
#1. 그룹화를 하면 조건식처럼 하나씩 mask를 만들어 조회하지 않아도 된다.
#2. 코드와 데이터의 재사용성이 증가한다.
#3. 그룹별 통계를 낼 수 있게 되어, 집단들의 특징을 파악하기 쉬운 구조가 된다.
#4. .pivot_table()을 사용할 때보다 수행능력/편의성에 이득이 있다.
# .groupby() vs .pivot_table()
pokemons.pivot_table(index='Type 1') #error
# pivot_table에는 numeric_only옵션이 없다.
# 따라서 경고를 안띄우려면 열 지정이나 메소드 실행 등 추가 데이터전처리가 필요하다.
# 하지만 group화된 데이터를 집계할 땐 numeric_only 옵션이 있다.
pokemons.pivot_table(index='Type 1', numeric_only=True) #error
type1_group.mean(numeric_only=True)
# 특정 그룹에 대한 집계 함수 수행
type1_group.get_group('Bug').mean(numeric_only=True)
DataFrame재구성 - 그룹화(groupby) 다중 열 그룹화와 agg()와 연계
# 실습 2 - 2개 열 그룹화
types = ['Type 1','Type 2']
types_groups = pokemons.groupby(by=['Type 1','Type 2'], dropna=False)
types_groups.groups # 멀티 인덱스처럼 튜플로 인덱스가 묶여있다.
# 여기서 집계함수를 하면
types_groups.mean(numeric_only=True)
# 조회 -> 멀티인덱싱 방법과 동일
types_groups.max().loc['Dark']
types_groups.max().loc[['Dark','Ghost']]
types_groups.max().loc[[('Bug','Fire')]]
# 특정 타입 조회, 멀티인덱싱 level 1
types_groups.max().loc[:,'Dark',:]
# 주의 - 그룹화되면 한 행의 각 열 데이터는 독립적
# "Bug-Electric 타입에서 제일 좋은 포켓몬을 찾으면 Joltik 인가?
types_groups.max()
# 하지만 실제로 찾아보면 일치하지 않는다.
pokemons[pokemons['Name'] == 'Joltik']
# 왜냐면 각 열에서 그룹화한 열은 여러개의 데이터가 하나의 보따리에 들어간 상태이며
# 보따리 안에서 max인 것을 반환한 것이기 때문
# 실습 3 - .groupby()이후 .agg()
# gropyby() 이후가 Dataframe은 아니지만 agg()를 사용할 수 있다.
pokemons.groupby('Type 1').agg({'Total':['max','min','mean']})
pokemons.groupby('Type 1').agg({'Total':['max','min','mean'], 'HP':['max','min','mean','std']}).round(2)
공부하며 어려웠던 내용
stack, melt, pivot 등 구조 관련 내용들을 이해하는데 어려웠다.
'TIL' 카테고리의 다른 글
| 데이터 분석 과정 학습 및 시각화 실습(1) (2) | 2024.01.08 |
|---|---|
| Python 프로그래밍 및 Pandas 활용 실습(5)-2 (1) | 2024.01.07 |
| Python 프로그래밍 및 Pandas 활용 실습(4) (1) | 2024.01.05 |
| Python 프로그래밍 및 Pandas 활용 실습(3)-2 (0) | 2024.01.04 |
| Python 프로그래밍 및 Pandas 활용 실습(3)-1 (2) | 2024.01.04 |