import numpy as np
import pandas as pd
13wk-2: Pandas – apply
, map
, applymap
1. 강의영상
2. Imports
3. apply
A. Motive
-
아래와 같은 상황이 있었다. (05wk-2의 숙제)
= pd.DataFrame({'A':[1,2,3,4]})
df df
A | |
---|---|
0 | 1 |
1 | 2 |
2 | 3 |
3 | 4 |
'A']].apply(np.mean) df[[
A 2.5
dtype: float64
'A'].apply(np.mean) df[
0 1.0
1 2.0
2 3.0
3 4.0
Name: A, dtype: float64
B. s.apply()
-
가능한 형태는 아래와 같다.
- 변환함수(스칼라입력,스칼라출력): 로그, 제곱
- 변환함수(벡터입력,벡터출력): 표준화, 정렬
- 집계함수(벡터입력,스칼라출력): 평균, 최대값
쓸모있는건 1 뿐이다.
# 예제1
– s.apply
+ 스칼라입력, 스칼라출력
= pd.Series([1,2,3])
s s
0 1
1 2
2 3
dtype: int64
apply(lambda x: -x) s.
0 -1
1 -2
2 -3
dtype: int64
이건 사실 아래의 동작으로 이해하면 된다.
1 -> -1
2 -> -2
3 -> -3
코드로는 아래와 같은 느낌
lambda x: -x)(i) for i in s] [(
[-1, -2, -3]
#
# 예제2
– s.apply
+ 벡터입력/스칼라출력(집계함수) // 가능은한데 사실상 스칼라입력,스칼라출력으로 해석해야함
= pd.Series([1,2,3])
s s
0 1
1 2
2 3
dtype: int64
apply(np.sum) # ?? s.
0 1
1 2
2 3
dtype: int64
- 에러는 안나지만 원하는 동작은 아님
이것은 사실 아래의 동작으로 이해할 수 있다.
1 -> sum(1) = 1
2 -> sum(2) = 2
3 -> sum(3) = 3
코드로는 아래의 느낌
sum(i) for i in s] [np.
[1, 2, 3]
#
# 예제3
– s.apply
+ 벡터입력/벡터출력 // 가능은 한데 사실상 스칼라입력,스칼라출력 함수로 해석해야함
= pd.Series([1,2,3])
s s
0 1
1 2
2 3
dtype: int64
apply(lambda x: x-np.mean(x)) s.
0 0.0
1 0.0
2 0.0
dtype: float64
- 에러는 안나지만 원하는 동작은 아님
이것은 사실 아래의 동작으로 이해할 수 있다.
1 -> 1-mean(1) = 0
2 -> 2-mean(2) = 0
3 -> 3-mean(3) = 0
코드로는 아래의 느낌
-np.mean(i) for i in s] [i
[0.0, 0.0, 0.0]
#
C. df.apply()
-
가능한 형태는 아래와 같다.
- 변환함수(벡터입력,벡터출력): 표준화, 정렬
- 집계함수(벡터입력,스칼라출력): 평균, 최대값
쓸모있는건 1,2 모두이다.
# 예제1
– df.apply
+ 스칼라입력, 스칼라출력 (불가능)
= pd.DataFrame({'X':[0.1,0.2,0.3],'Y':[-0.1,-0.2,-0.3]})
df df
X | Y | |
---|---|---|
0 | 0.1 | -0.1 |
1 | 0.2 | -0.2 |
2 | 0.3 | -0.3 |
apply(lambda x: 'pos' if x>0 else 'neg') df.
ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().
# 예제2
– df.apply
+ 스칼라입력,스칼라출력? 벡터입력,벡터출력!!
= pd.DataFrame({'X':[0.1,0.2,0.3],'Y':[-0.1,-0.2,-0.3]})
df df
X | Y | |
---|---|---|
0 | 0.1 | -0.1 |
1 | 0.2 | -0.2 |
2 | 0.3 | -0.3 |
apply(lambda x: x**2) # 이건 스칼라입력, 스칼라출력 아니고 벡터입력 벡터출력으로 컴퓨터가 해석함 df.
X | Y | |
---|---|---|
0 | 0.01 | 0.01 |
1 | 0.04 | 0.04 |
2 | 0.09 | 0.09 |
이것은 사실 아래의 동작으로 이해할 수 있다.
df['X'] -> (df['X'])**2
df['Y'] -> (df['Y'])**2
코드로는 아래의 느낌이다.
lambda x: x**2)(df[i]) for i in df] [(
[0 0.01
1 0.04
2 0.09
Name: X, dtype: float64,
0 0.01
1 0.04
2 0.09
Name: Y, dtype: float64]
#
# 예제3
– df.apply
+ 벡터입력,스칼라출력(집계함수)
= pd.DataFrame({'X':[0.1,0.2,0.3],'Y':[-0.1,-0.2,-0.3]})
df df
X | Y | |
---|---|---|
0 | 0.1 | -0.1 |
1 | 0.2 | -0.2 |
2 | 0.3 | -0.3 |
apply(np.sum) df.
X 0.6
Y -0.6
dtype: float64
#
# 예제4
– df.apply
+ 벡터입력,스칼라출력(집계함수)
= pd.DataFrame({'X':[0.1,0.2,0.3],'Y':[-0.1,-0.2,-0.3]})
df df
X | Y | |
---|---|---|
0 | 0.1 | -0.1 |
1 | 0.2 | -0.2 |
2 | 0.3 | -0.3 |
apply(np.sum,axis=1) df.
0 0.0
1 0.0
2 0.0
dtype: float64
s.apply
에서는axis
가 유효한 인자가 아니지만df.apply
에서는axis
가 유효한 입력이고 디폴트값은 0이다.
#
# 예제5
– df.apply
+ 벡터입력,벡터출력
= pd.DataFrame({'X':[1,2,3],'Y':[4,5,6]})
df df
X | Y | |
---|---|---|
0 | 1 | 4 |
1 | 2 | 5 |
2 | 3 | 6 |
apply(lambda x: x-np.mean(x)) df.
X | Y | |
---|---|---|
0 | -1.0 | -1.0 |
1 | 0.0 | 0.0 |
2 | 1.0 | 1.0 |
apply(lambda x: x-np.mean(x),axis=1) df.
X | Y | |
---|---|---|
0 | -1.5 | 1.5 |
1 | -1.5 | 1.5 |
2 | -1.5 | 1.5 |
#
# 예제6
– df.apply
+ 벡터입력, 벡터출력
= pd.DataFrame({'X':[ 3.285, 0.328, -1.261],'Y':[ 1.068, 0.145, -0.222]})
df df
X | Y | |
---|---|---|
0 | 3.285 | 1.068 |
1 | 0.328 | 0.145 |
2 | -1.261 | -0.222 |
apply(np.sort) df.
X | Y | |
---|---|---|
0 | -1.261 | -0.222 |
1 | 0.328 | 0.145 |
2 | 3.285 | 1.068 |
apply(np.sort, axis=1) df.
0 [1.068, 3.285]
1 [0.145, 0.328]
2 [-1.261, -0.222]
dtype: object
apply(lambda x: x*0+np.sort(x), axis=1) # 그다지 안중요한 트릭.. df.
X | Y | |
---|---|---|
0 | 1.068 | 3.285 |
1 | 0.145 | 0.328 |
2 | -1.261 | -0.222 |
#
4. map
-
그냥 모든 원소에 동일적용
A. s.map()
-
가능한 형태는 아래와 같다.
- 변환함수(스칼라입력,스칼라출력): 로그, 제곱
- 변환함수(벡터입력,벡터출력): 표준화, 정렬
- 집계함수(벡터입력,스칼라출력): 평균, 최대값
- 딕셔너리
쓸모있는건 1,4 이다. 특히 4는 특정상황에서 매우 쓸모있음
# 예제1
– s.map
+ 스칼라입력,스칼라출력
= pd.Series(['A','B','B','B','A'])
s s
0 A
1 B
2 B
3 B
4 A
dtype: object
map(lambda x: x.lower()) s.
0 a
1 b
2 b
3 b
4 a
dtype: object
#
# 예제2
– s.map
+ 스칼라입력,스칼라출력
= pd.Series([1,3,4,2])
s s
0 1
1 3
2 4
3 2
dtype: int64
map(lambda x: x**2) s.
0 1
1 9
2 16
3 4
dtype: int64
#
# 예제3
– s.map
+ 벡터입력,스칼라출력 // 가능은한데 사실 스칼라입력,스칼라출력으로 해석해야함
= pd.Series([1,3,4,2])
s s
0 1
1 3
2 4
3 2
dtype: int64
map(np.sum) s.
0 1
1 3
2 4
3 2
dtype: int64
#
# 예제4
– s.map
+ 벡터입력,벡터출력 // 가능은한데 사실 스칼라입력,스칼라출력으로 해석해야함
= pd.Series([1,3,4,2])
s s
0 1
1 3
2 4
3 2
dtype: int64
map(lambda x: x-np.mean(x)) s.
0 0.0
1 0.0
2 0.0
3 0.0
dtype: float64
#
# 예제5
– s.map
+ 딕셔너리
= pd.Series(['A','B','B','B','A'])
s s
0 A
1 B
2 B
3 B
4 A
dtype: object
map({'A':'A+','B':'B0'}) s.
0 A+
1 B0
2 B0
3 B0
4 A+
dtype: object
#
B. df.map()
= df.applymap()
코랩에서 실습할경우
map() df.
이 동작하지 않습니다. 대신 아래와 같이 applymap
이 동작합니다.
df.applymap()
이것은 코랩에서 기본으로 설치되어있는 pandas의 버전이 너무 낮아서 생기는 문제입니다. 따라서 코랩을 쓰시는 분들은 아래의 강의노트들을 df.applymap()
으로 바꿔서 실습하시기 바랍니다.
만약 코랩에서도 df.map()
을 사용하시려면 pandas를 높은버전으로 새로 설치하고 사용하시면 됩니다. 즉
1. 코랩커널재시작 (컴퓨터 다시 할당)
2. `import pandas as pd` 를 하기 전에 `!pip install pandas -U`를 이용하여 판다스를 최신버전으로 재설치
3. 판다스를 임포트 하고 (`import pandas as pd`) 실습
와 같이 하시면 됩니다.
-
가능한 형태는 아래와 같다.
- 변환함수(스칼라입력,스칼라출력): 로그, 제곱
- 집계함수(벡터입력,스칼라출력)
1만 쓸모있다. 여기에서 df.map(변환함수)
꼴은 사실 df.applymap(변환함수)
와 가능이 같다.
# 예제1
– df.map
+ 스칼라입력,스칼라출력
= pd.DataFrame({'A':[2143,2143],'B':['-',3456]})
df df
A | B | |
---|---|---|
0 | 2143 | - |
1 | 2143 | 3456 |
map(lambda x: 0 if x == '-' else x) df.
A | B | |
---|---|---|
0 | 2143 | 0 |
1 | 2143 | 3456 |
#
# 예제2
– df.map
+ 벡터입력,벡터출력 // 불가능해
= pd.DataFrame({'A':np.random.randn(5), 'B':np.random.randn(5)+5})
df df
A | B | |
---|---|---|
0 | -0.474722 | 4.616944 |
1 | -0.123587 | 5.941885 |
2 | 1.011905 | 2.960560 |
3 | 0.306291 | 3.769737 |
4 | -1.231884 | 6.354306 |
map(np.sort) # 불가능.. df.
AxisError: axis -1 is out of bounds for array of dimension 0
#
# 예제3
– df.map
+ 벡터입력,스칼라출력(집계함수) // 가능하긴한데 사실 스칼라입력,스칼라출력으로 해석해야함
= pd.DataFrame({'A':np.random.randn(5), 'B':np.random.randn(5)+5})
df df
A | B | |
---|---|---|
0 | -0.331755 | 4.738557 |
1 | 1.064457 | 3.152778 |
2 | 1.096634 | 2.535014 |
3 | 0.586018 | 5.692224 |
4 | -1.619656 | 5.566970 |
map(np.mean) # 사실상 스칼라입력,스칼라출력으로 봐야함 df.
A | B | |
---|---|---|
0 | -0.331755 | 4.738557 |
1 | 1.064457 | 3.152778 |
2 | 1.096634 | 2.535014 |
3 | 0.586018 | 5.692224 |
4 | -1.619656 | 5.566970 |
#
# 예제4
– df.map
+ 딕셔너리 // 불가능
= pd.DataFrame({'guebin':[0,1,0,1,0,1],'hynn':[0,1,1,1,1,1]})
df df
guebin | hynn | |
---|---|---|
0 | 0 | 0 |
1 | 1 | 1 |
2 | 0 | 1 |
3 | 1 | 1 |
4 | 0 | 1 |
5 | 1 | 1 |
map({0:'fail',1:'pass'}) df.
TypeError: the first argument must be callable
#
5. HW
아래의 자료가 있다고 하자.
= pd.DataFrame({'guebin':[0,1,0,1,0,1],'hynn':[0,1,1,1,1,1]})
df df
guebin | hynn | |
---|---|---|
0 | 0 | 0 |
1 | 1 | 1 |
2 | 0 | 1 |
3 | 1 | 1 |
4 | 0 | 1 |
5 | 1 | 1 |
이 자료를 lambda
와 df.applymap
을 이용하여 아래와 같이 변경하라.
#
guebin | hynn | |
---|---|---|
0 | fail | fail |
1 | pass | pass |
2 | fail | pass |
3 | pass | pass |
4 | fail | pass |
5 | pass | pass |