2024.01.01 TIL
학습주제
- pandas 입문, 데이터셋을 통한 다양한 예제, 깊은복사 vs 얉은복사
데이터셋 준비하기
- excel 파일 읽고 데이터프레임(Dataframe)만들기
df = pd.read_excel('./slamdunk_player_stats.xlsx')
df
데이터프레임 구조 가볍게 보기
- 앞쪽 뒤쪽 데이터를 확인하는 head(), tail()
df.head()
| 이름 | 포지션 | 소속 | 신장 | 체중 |
|---|
| 0 | 채치수 | 센터 | 북산 | 197 | 90 |
| 1 | 강백호 | 파워포워드 | 북산 | 189 | 83 |
| 2 | 서태웅 | 스몰포워드 | 북산 | 187 | 75 |
| 3 | 정대만 | 슈팅가드 | 북산 | 184 | 70 |
| 4 | 정병욱 | 센터 | 북산 | 180 | 78 |
💡 5줄이 기본이다.
df.head(3)
| 이름 | 포지션 | 소속 | 신장 | 체중 |
|---|
| 0 | 채치수 | 센터 | 북산 | 197 | 90 |
| 1 | 강백호 | 파워포워드 | 북산 | 189 | 83 |
| 2 | 서태웅 | 스몰포워드 | 북산 | 187 | 75 |
df.tail(3)
| 이름 | 포지션 | 소속 | 신장 | 체중 |
|---|
| 23 | 이정환 | 포인트가드 | 해남 | 184 | 79 |
| 24 | 김수겸 | 포인트가드 | 상양 | 178 | 66 |
| 25 | 김준일 | 포인트가드 | 삼포 | 186 | 74 |
행 조회 (loc, iloc, slicing)
- Row(행) 데이터를 선택(조회)하기 - index, slice, loc, iloc
df.loc[1]
이름 강백호
포지션 파워포워드
소속 북산
신장 189
체중 83
골밑슛 80
미들슛 32
3점슛 21
덩크슛 93
레이업 90
패스 54
파워 90
방해저항 58
도약 100
런닝 96
드리블 56
리바운드 107
블록 88
방해 85
스틸 66
Name: 1, dtype: object
df.loc[[1]]
💡 데이터프레임 형식으로 보고 싶으면 []안에 넣으면 된다.
| 이름 | 포지션 | 소속 | 신장 | 체중 | 골밑슛 |
|---|
| 1 | 강백호 | 파워포워드 | 북산 | 189 | 83 | 80 |
df.loc[[3, 6, 9]]
| 이름 | 포지션 | 소속 | 신장 | 체중 |
|---|
| 3 | 정대만 | 슈팅가드 | 북산 | 184 | 70 |
| 6 | 신오일 | 슈팅가드 | 북산 | 170 | 63 |
| 9 | 고민구 | 센터 | 해남 | 191 | 80 |
df.loc[[3, 6, 9], ["이름", "체중"]]
df.loc[3:7, "이름":"체중"]
| 이름 | 포지션 | 소속 | 신장 |
|---|
| 3 | 정대만 | 슈팅가드 | 북산 | 184 |
| 4 | 정병욱 | 센터 | 북산 | 180 |
| 5 | 권준호 | 스몰포워드 | 북산 | 178 |
| 6 | 신오일 | 슈팅가드 | 북산 | 170 |
| 7 | 송태섭 | 포인트가드 | 북산 | 168 |
df.loc[3:8:2]
df.loc[:, ["이름", "포지션", "소속", "리바운드"]]
df.iloc[3:7, 0:5]
인덱스 3번부터 6번까지 0~4의 열의 데이터가 출력된다.
df['이름']
0 채치수
1 강백호
2 서태웅
3 정대만
4 정병욱
5 권준호
6 신오일
7 송태섭
8 이달재
9 고민구
10 장권혁
11 신준섭
12 변덕규
13 성현준
14 윤대협
15 황태산
16 허태환
17 구대철
18 김용
19 전호창
20 안영수
21 홍익현
22 백정태
23 이정환
24 김수겸
25 김준일
Name: 이름, dtype: object
df['이름'].head()
0 채치수
1 강백호
2 서태웅
3 정대만
4 정병욱
Name: 이름, dtype: object
df[['이름']]
💡 역시 데이터 프레임 형식으로 보고 싶으면 [] 안에 삽입하면 된다.
df[['이름', '신장', '소속']].head(7)
Series
- 데이터프레임 = 시리즈 n개 이상 이루어져있다.
💡 특정 부분을 볼 수 있는데, 이 때 데이터프레임, 시리즈로도 볼 수 있다!
- 수정을 할 수 있다.
- 데이터 프레임 특정 한 줄 수정하고 싶다.
- 데이터 프레임_시리즈 = 동일한 규모에 시리즈
- **조건에 따라 특정 데이터만 조회할 때 시리즈를 사용**
기술 통계
df.describe()
| 신장 |
|---|
| count | 26.000000 |
| mean | 183.384615 |
| std | 10.350177 |
| min | 160.000000 |
| 25% | 178.000000 |
| 50% | 185.000000 |
| 75% | 189.750000 |
| max | 202.000000 |
- describe를 사용하면 여러가지 통계를 출력할 수 있다.
- count = 합
- mean = 평균
- std = 표준편차
- min = 최솟값
- max = 최댓값
- 50% = min과 max사이의 50퍼센트 값 (중간 값)
문자 탐색 (str 활용)
df["이름"].str
df["이름"].str.contains("정")
0 False
1 False
2 False
3 True
4 True
5 False
6 False
7 False
8 False
9 False
10 False
11 False
12 False
13 False
14 False
15 False
16 False
17 False
18 False
19 False
20 False
21 False
22 True
23 True
24 False
25 False
Name: 이름, dtype: bool
- 이름에 정이 들어가면 True 아니면 False를 반환하도록 한다.
sum(df["이름"].str.contains("정"))
4
df["이름"].str.contains("정").sum()
4
df[df["이름"].str.contains("정")]
- 이름에 정이 들어간 경우의 데이터프레임만 출력하도록 한다.
| 이름 | 포지션 | 소속 | 신장 |
|---|
| 3 | 정대만 | 슈팅가드 | 북산 | 184 |
| 4 | 정병욱 | 센터 | 북산 | 180 |
| 22 | 백정태 | 포인트가드 | 능남 | 179 |
| 23 | 이정환 | 포인트가드 | 해남 | 184 |
df["이름"].str[0]=="정"
- 정씨인 사람만 출력하도록 인덱스를 이용하였다.
df[df["이름"].str[0]=="정"]
| 이름 | 포지션 | 소속 | 신장 |
|---|
| 3 | 정대만 | 슈팅가드 | 북산 | 184 |
| 4 | 정병욱 | 센터 | 북산 | 180 |
수치 탐색과 산술 연산
데이터프레임의 각 값은 for문을 거치지 않고도 각자 수행할 수 있다. 이를 이용해보자.
df['신장'] > 180
0 True
1 True
2 True
3 True
4 False
5 False
6 False
7 False
8 False
9 True
10 True
11 True
12 True
13 True
14 True
15 True
16 True
17 True
18 True
19 False
20 False
21 False
22 False
23 True
24 False
25 True
Name: 신장, dtype: bool
df['신장']+100
별도의 DataFrame 생성
원하는 DataFrame을 생성하고 변수에 저장해보자
df_center = df[df["포지션"]=="센터"]
df_center
| 이름 | 포지션 | 소속 | 신장 |
|---|
| 0 | 채치수 | 센터 | 북산 | 197 |
| 4 | 정병욱 | 센터 | 북산 | 180 |
| 9 | 고민구 | 센터 | 해남 | 191 |
| 12 | 변덕규 | 센터 | 능남 | 202 |
| 13 | 성현준 | 센터 | 상양 | 197 |
apply()
df_power["신장"].apply(lambda x : x+100)
df_power["신장"] = df_power["신장"].apply(lambda x : x+100)
df_power.head()
| 이름 | 신장 | 체중 | 패스 |
|---|
| 0 | 채치수 | 297 | 90 | 70 |
| 1 | 강백호 | 289 | 83 | 54 |
| 2 | 서태웅 | 287 | 75 | 100 |
| 3 | 정대만 | 284 | 70 | 82 |
| 4 | 정병욱 | 280 | 78 | 78 |
df_power["new_신장"] = df_power["신장"].apply(lambda x : x+100)
df_power.head()
| 이름 | 신장 | 체중 | 패스 | new_신장 |
|---|
| 0 | 채치수 | 297 | 90 | 70 | 397 |
| 1 | 강백호 | 289 | 83 | 54 | 389 |
| 2 | 서태웅 | 287 | 75 | 100 | 387 |
| 3 | 정대만 | 284 | 70 | 82 | 384 |
| 4 | 정병욱 | 280 | 78 | 78 | 380 |
numbers = [0, 1, 2, 3, 4, 5, 6, 7]
for n in numbers:
print(n)
numbers.remove(n)
print(numbers)
0
2
4
6
[1, 3, 5, 7]
🚫 이런 경우 n이 numbers의 원본데이터와 직접 적으로 연결되어 있기 때문에 n을 삭제하면
numbers에서도 삭제됨
numbers = [0, 1, 2, 3, 4, 5, 6, 7]
for n in numbers.copy():
print(n)
numbers.remove(n)
print(numbers)
0
1
2
3
4
5
6
7
[]
👍 copy를 사용하면 해결할 수 있음
열(col) 삭제 - del df['열이름']
del df_power_new['패스평가']
행(row)삭제 - df.drop([index])
database sql의 drop과 비슷
파이썬 list index(숫자로만 이뤄진 것이 아닌) database index로 생각(숫자가 아니여도 되는)
df_power_new.drop(0).head()
💡 비파과적인 처리를 하면 원본데이터에는 아무런 처리가 적용되지 않음
df_power_new = df_power_new.drop(5)
df_power_new.head()
df_power_new.drop(2, inplace=True)
df_power_new.head()
💡 위의 2가지 방법으로 파괴적인 처리를 할 수 있음
열 이름 변경 -.rename(columns = {'기존':'변경'}, inplace=True/False)
• .rename의 docstring을 보면 index 등도 변경할 수 있다.
df_power_new.rename(columns={'신장':'키', '체중':'몸무게'})
df_power_new.head()
- 신장이 키로 이름이 바뀌고, 체중이 몸무게로 이름이 바뀐다.
df_power_new.rename(columns={'신장':'키', '체중':'몸무게'}, inplace=True)
df_power_new.head()
inplace**=True** 를 추가하여 파괴적인 처리 진행
정렬 - .sort_index(), .sort_values()
df_power_new.sort_values(by='키', inplace=True)
df_power_new.head()
| 이름 | 키 | 몸무게 | 패스 |
|---|
| 21 | 홍익현 | 160 | 42 | 84 |
| 8 | 이달재 | 164 | 57 | 98 |
| 7 | 송태섭 | 168 | 59 | 100 |
| 6 | 신오일 | 170 | 63 | 78 |
| 20 | 안영수 | 174 | 62 | 85 |
df_power_new.sort_values(by='키', inplace=True, ascending=False)
df_power_new.head()
| 이름 | 키 | 몸무게 | 패스 |
|---|
| 12 | 변덕규 | 202 | 90 | 53 |
| 13 | 성현준 | 197 | 83 | 80 |
| 17 | 구대철 | 196 | 82 | 33 |
| 9 | 고민구 | 191 | 80 | 76 |
| 14 | 윤대협 | 190 | 79 | 92 |
피벗 테이블 - pivot_table
엑셀, 스프레드 시트의 피봇 테이블과 동일합니다.
pivot_df = pd.pivot_table(df, index='신장', values=['3점슛', '골밑슛'], aggfunc='mean')
pivot_df.round(2)
| 신장 | 3점슛 | 골밑슛 |
|---|
| 160 | 105.0 | 87.00 |
| 164 | 58.0 | 82.00 |
| 168 | 62.0 | 80.00 |
| 170 | 80.0 | 83.00 |
| 174 | 95.0 | 85.00 |
애초에 pandas의 표기 수치를 바꾸는 방법도 있다.
pd.options.display.float_format = '{:.2f}'.format
얕은 복사(Shallow Copy) vs 깊은 복사(Deep Copy)