01 Pandas의 행 / 열 결합
02 수학 / 통계 함수 및 메서드
-01 기본함수
-02 math 함수
-03 numpy 함수 / 메서드
-04 pandas 함수 / 메서드
03 결측치
-01 표현방법
-02 확인
-03 수정
-04 결측치의 처리
01 Pandas의 행 / 열 결합
import pandas as pd
from pandas import Series, DataFrame
pd.concat(objs, # 결합대상 (리스트로 묶어서 전달)
axis = 0, # 결합방향 (0 : 세로방향, 1 : 가로방향)
join = 'outer', # 결합방식 (기본은 모두 출력, inner : 공통키만 출력)
ignore_index = False) # 결합 후 인덱스 재배치 여부
하나의 행을 추가해야할 시, 여러 행을 추가해야할 시 pd.concat을 사용한다.
concat은 양방향 모두 결합이 가능하나 Series의 경우 DataFrame으로의 변환이 필요하다.
ex) 행/열 결합
pd.concat([DataFrame(arr3), DataFrame(arr4)])
pd.concat([DataFrame(arr3), DataFrame(arr4)])
Out[40]:
0 1 2 3
0 1 2 3 4
1 5 6 7 8
0 10 20 30 40
1 50 60 70 80
또한 index가 그대로 따라오는 문제가 있기 때문에 ignore_index = True 를 통해 index 를 재지정해준다.
pd.concat([DataFrame(arr3), DataFrame(arr4)], axis = 1, ignore_index=True)
pd.concat([DataFrame(arr3), DataFrame(arr4)], ignore_index=True)
Out[41]:
0 1 2 3
0 1 2 3 4
1 5 6 7 8
2 10 20 30 40
3 50 60 70 80
pd.concat([DataFrame(arr3), DataFrame(arr4)], axis = 1, ignore_index=True)
pd.concat([DataFrame(arr3), DataFrame(arr4)], axis = 1, ignore_index=True)
Out[44]:
0 1 2 3 4 5 6 7
0 1 2 3 4 10 20 30 40
1 5 6 7 8 50 60 70 80
ex) 데이터프레임 결합 (행 결합)
df1 = DataFrame({'name' : ['smith','allen'], 'sal':[1000,2000]})
df2 = DataFrame({'name' : ['king','ford'], 'sal':[3000,4000]})
df12 = pd.concat([df1,df2], ignore_index=True)
df12
Out[59]:
name sal
0 smith 1000
1 allen 2000
2 king 3000
3 ford 4000
ex) 데이터프레임 결합 (컬럼 결합)
df3 = DataFrame({'name':['smith','allen'], 'sal':[1000,2000]})
df4 = DataFrame({'name':['smith','allen'], 'deptno':[10,20]]})
df34 = pd.concat([df3,df4], axis=1)
df34
Out[67]:
name sal name deptno
0 smith 1000 smith 10
1 allen 2000 allen 20
ex) 서로 다른 키를 갖는 데이터프레임 결합
df5 = DataFrame({'name':['smith','allen'], 'sal':[1000,2000]})
df6 = DataFrame({'name':['ford','scott'], 'deptno':[10,20]})
pd.concat([df5, df6]) # 서로 다른 키 컬럼도 모두 출력
pd.concat([df5, df6], join = 'outer') # 위와 결과 동일
pd.concat([df5, df6], join = 'inner') # 양쪽 데이터프레임의 같은 키만 출력됨
pd.concat([df5, df6]) # 서로 다른 키 컬럼도 모두 출력
Out[71]:
name sal deptno
0 smith 1000.0 NaN
1 allen 2000.0 NaN
0 ford NaN 10.0
1 scott NaN 20.0
pd.concat([df5, df6], join = 'outer') # 위와 결과 동일
Out[72]:
name sal deptno
0 smith 1000.0 NaN
1 allen 2000.0 NaN
0 ford NaN 10.0
1 scott NaN 20.0
pd.concat([df5, df6], join = 'inner') # 양쪽 데이터프레임의 같은 키만 출력됨
Out[73]:
name
0 smith
1 allen
0 ford
1 scott
[연습문제]
emp_1.xlsx, emp_2.xlsx, emp_3.xlsx 파일 모두 합치기
emp1 = pd.read_excel('emp_1.xlsx')
emp2 = pd.read_excel('emp_2.xlsx')
emp3 = pd.read_excel('emp_3.xlsx')
emp1.head()
Out[120]:
EMPNO ENAME JOB
0 7369 SMITH CLERK
1 7499 ALLEN SALESMAN
2 7521 WARD SALESMAN
3 7566 JONES MANAGER
4 7654 MARTIN SALESMAN
emp2.head()
Out[121]:
EMPNO SAL COMM DEPTNO
0 7369 800 NaN 20
1 7499 1600 300.0 30
2 7521 1250 500.0 30
3 7566 2975 NaN 20
4 7654 1250 1400.0 30
emp3.head()
Out[122]:
EMPNO ENAME JOB SAL COMM DEPTNO
0 9999 PARK CLERK 5000 100.0 10
1 9998 KIM MANAGER 4800 20.0 20
2 9997 YOO ANALYST 6000 500.0 30
3 9996 JUNG CLERK 5100 NaN 10
4 9995 HYUN MANAGER 4900 NaN 20
# index를 기준으로 결합하기 때문에 같은 키를 가지고 있는 경우 테이블간 index 순서가 다를 때 conat보다는 merge를 사용하는 것이 좋을 수도 있다.
(예를 들어 0번 index에 a테이블은 'smith', b테이블은 'miller'라면 smith와 miller의 정보가 결합된다.)
pd.concat([emp1, emp2], axis = 1) # 단순 컬럼 결합 (empno 컬럼 중복, 각 데이터프레임 위치 상관없이 결합)
pd.concat([emp1, emp2], axis = 1) # 단순 컬럼 결합 (empno 컬럼 중복, 각 데이터프레임 위치 상관없이 결합)
Out[126]:
EMPNO ENAME JOB EMPNO SAL COMM DEPTNO
0 7369 SMITH CLERK 7369 800 NaN 20
1 7499 ALLEN SALESMAN 7499 1600 300.0 30
2 7521 WARD SALESMAN 7521 1250 500.0 30
3 7566 JONES MANAGER 7566 2975 NaN 20
4 7654 MARTIN SALESMAN 7654 1250 1400.0 30
5 7698 BLAKE MANAGER 7698 2850 NaN 30
6 7782 CLARK MANAGER 7782 2450 NaN 10
7 7788 SCOTT ANALYST 7788 3000 NaN 20
8 7839 KING PRESIDENT 7839 5000 NaN 10
9 7844 TURNER SALESMAN 7844 1500 0.0 30
10 7876 ADAMS CLERK 7876 1100 NaN 20
11 7900 JAMES CLERK 7900 950 NaN 30
12 7902 FORD ANALYST 7902 3000 NaN 20
13 7934 MILLER CLERK 7934 1300 NaN 10
emp12 = pd.merge(emp1, emp2) # empno 기준으로 결합됨
emp12
Out[128]:
EMPNO ENAME JOB SAL COMM DEPTNO
0 7369 SMITH CLERK 800 NaN 20
1 7499 ALLEN SALESMAN 1600 300.0 30
2 7521 WARD SALESMAN 1250 500.0 30
3 7566 JONES MANAGER 2975 NaN 20
4 7654 MARTIN SALESMAN 1250 1400.0 30
5 7698 BLAKE MANAGER 2850 NaN 30
6 7782 CLARK MANAGER 2450 NaN 10
7 7788 SCOTT ANALYST 3000 NaN 20
8 7839 KING PRESIDENT 5000 NaN 10
9 7844 TURNER SALESMAN 1500 0.0 30
10 7876 ADAMS CLERK 1100 NaN 20
11 7900 JAMES CLERK 950 NaN 30
12 7902 FORD ANALYST 3000 NaN 20
13 7934 MILLER CLERK 1300 NaN 10
pd.concat([emp12, emp3], ignore_index=True) # 행 결합
pd.concat([emp12, emp3], ignore_index=True)
Out[129]:
EMPNO ENAME JOB SAL COMM DEPTNO
0 7369 SMITH CLERK 800 NaN 20
1 7499 ALLEN SALESMAN 1600 300.0 30
2 7521 WARD SALESMAN 1250 500.0 30
3 7566 JONES MANAGER 2975 NaN 20
4 7654 MARTIN SALESMAN 1250 1400.0 30
5 7698 BLAKE MANAGER 2850 NaN 30
6 7782 CLARK MANAGER 2450 NaN 10
7 7788 SCOTT ANALYST 3000 NaN 20
8 7839 KING PRESIDENT 5000 NaN 10
9 7844 TURNER SALESMAN 1500 0.0 30
10 7876 ADAMS CLERK 1100 NaN 20
11 7900 JAMES CLERK 950 NaN 30
12 7902 FORD ANALYST 3000 NaN 20
13 7934 MILLER CLERK 1300 NaN 10
14 9999 PARK CLERK 5000 100.0 10
15 9998 KIM MANAGER 4800 20.0 20
16 9997 YOO ANALYST 6000 500.0 30
17 9996 JUNG CLERK 5100 NaN 10
18 9995 HYUN MANAGER 4900 NaN 20
19 9994 CHOI ANALYST 6100 NaN 30
02 수학 / 통계 함수 및 메서드
-01 기본함수
dir(__builtins__)
abs() # 절댓값
round() # 반올림
min() # 최소
max() # 최대
sum() # 더하기
pow() # 거듭제곱
any() # 참 여부 (이 중에 참이 있는가)
all() # 참 여부 (모두 참인가)
-02 math 함수
import math
dir(math)
math.trunc()
math.ceil()
math.floor()
math.sqrt()
math.log()
-03 numpy 함수 / 메서드
import numpy as np
arr1 = np.array([1,2,3,4,5])
dir(np) # numpy 함수목록
dir(arr1) # numpy 객체 호출 가능한 메서드 목록
np.sqrt(arr1)
np.exp()
np.log()
np.log10()
np.log1p()
※ 로그 스케일링(Log Scaling)은 회귀분석에서 종속변수(y)나 독립변수(x)가 한쪽으로 심하게 치우친 분포를 가질 때 자주 사용하는 데이터 변환 기법이다.
특히 값의 범위가 매우 크거나 이상치의 영향이 큰 경우, 로그 변환을 적용하면 데이터 분포를 보다 안정적으로 만들고 모델의 성능을 개선하는 데 도움이 된다.
다만 log() 함수는 입력값이 반드시 0보다 커야 하므로, 데이터에 0이 포함된 경우에는 일반적으로 다음과 같은 형태로 변환한다.
log(x + 1)
이 방식은 0 값을 유지하면서도 로그 변환의 효과를 적용할 수 있어 실무에서 많이 사용된다.
단, 로그 변환은 데이터에 음수 값이 없다는 가정하에서 적용해야 한다.
np.log1p() # np.log(arr1+1)와 같음
np.abs()
np.round()
np.ceil()
np.floor()
np.sum()
np.mean()
np.median() # 중앙값
np.var() # 분산
np.std() # 표준편차
np.min()
np.max()
np.cumsum() # 누적합
np.cumprod() # 누적곱
np.percentile(data, 25) # Q1
# ** 메서드
arr1.sum()
arr1.mean()
arr1.var()
-04 pandas 함수 / 메서드
import pandas as pd
from pandas import Series, DataFrame
s1 = Series(arr1)
s2 = Series([1,1,1,1,2,3])
df1 = DataFrame(np.arange(1,21).reshape(5,4), columns = list('ABCD'))
dir(pd) # pandas 함수목록
dir(arr1) # pandas 객체 호출 가능한 매서드 목록
s1.sum()
s1.var()
s1.std()
s1.median()
s2.mode().iloc[0] # 다중최빈값을 갖는 경우 때문에 Series로 리턴되므로 iloc[0]을 써서 스칼라로 리턴
df1.corr() # 각 컬럼별 피어슨 상관계수 출력
df1.corr() # 각 컬럼별 피어슨 상관계수 출력
Out[170]:
A B C D
A 1.0 1.0 1.0 1.0
B 1.0 1.0 1.0 1.0
C 1.0 1.0 1.0 1.0
D 1.0 1.0 1.0 1.0
df1.cov() # 각 컬럼별 공분산 출력
df1.cov() # 각 컬럼별 공분산 출력
Out[171]:
A B C D
A 40.0 40.0 40.0 40.0
B 40.0 40.0 40.0 40.0
C 40.0 40.0 40.0 40.0
D 40.0 40.0 40.0 40.0
df1.describe() # 수치컬럼 요약 정보 (수, 평균, 표준편차, 최솟값, 사분위수, 최댓값)
df1.describe()
Out[169]:
A B C D
count 5.000000 5.000000 5.000000 5.000000
mean 9.000000 10.000000 11.000000 12.000000
std 6.324555 6.324555 6.324555 6.324555
min 1.000000 2.000000 3.000000 4.000000
25% 5.000000 6.000000 7.000000 8.000000
50% 9.000000 10.000000 11.000000 12.000000
75% 13.000000 14.000000 15.000000 16.000000
max 17.000000 18.000000 19.000000 20.000000
** numpy / pandas 수학통계 함수 비교
np.nan # 결측치 표현방법
a1 = np.array([1,2,3,4,5])
a2 = np.array([1,2,np.nan,4,5])
s1 = Series(a1)
s2 = Series(a2)
1) 분산결과
a1.var() # 2.0
np.var(a1) # 2.0
s1.var() # 2.5
numpy의 var와 pandas의 var가 다르게 출력된다.
numpy의 경우 옵션을 일부 조정해야 할 필요가 있어 pandas를 대체로 많이 사용한다.
# ** 분산 계산 : sum((값-평균)^2/n-1)
전체 샘플의 갯수로 나누어준다. (numpy)
pandas의 경우 통계분산이기 때문에 n-1로 나누어준다.
sum((a1 - a1.mean())**2)/len(a1) # numpy var 계산 방식
sum((a1 - a1.mean())**2)/(len(a1)-1) # pandas var 계산 방식
sum((a1 - a1.mean())**2)/len(a1) # numpy var 계산 방식
Out[185]: np.float64(2.0)
sum((a1 - a1.mean())**2)/(len(a1)-1) # pandas var 계산 방식
Out[186]: np.float64(2.5)
+) np.var?
기본적으로 자유도에 대한 옵션(ddof)이 0으로 설정되어 있음을 확인할 수 있다.
Signature:
np.var(
a,
axis=None,
dtype=None,
out=None,
ddof=0,
keepdims=<no value>,
*,
where=<no value>,
mean=<no value>,
correction=<no value>,
)
따라서 ddof = 1로 설정하면 pandas의 분산과 같은 결과가 나온다.
np.var(a1, ddof = 1)
np.var(a1, ddof = 1)
Out[188]: np.float64(2.5)
+) s1.var?
Signature:
s1.var(
axis: 'Axis | None' = None,
skipna: 'bool' = True,
ddof: 'int' = 1,
numeric_only: 'bool' = False,
**kwargs,
)
Docstring:
Return unbiased variance over requested axis.
Normalized by N-1 by default. This can be changed using the ddof argument.
따라서 ddof = 0으로 설정하면 numpy의 분산과 같은 결과가 나온다.
s1.var(ddof = 0)
s1.var(ddof = 0)
Out[193]: 2.0
2) NA 무시 옵션
a2.sum() # NA로 리턴 (skipna 옵션이 없어서 NA를 포함한 객체의 연산 결과는 항상 NA로 리턴)
s2.sum() # 12 (skipna = True가 기본값이므로 NA를 무시한 결과가 리턴)
a2.sum()
Out[194]: np.float64(nan)
s2.sum()
Out[195]: np.float64(12.0)
s2.sum?
Signature:
s2.sum(
axis: 'Axis | None' = None,
skipna: 'bool' = True,
numeric_only: 'bool' = False,
min_count: 'int' = 0,
**kwargs,
)
Docstring:
Return the sum of the values over the requested axis.
This is equivalent to the method ``numpy.sum``.
numpy의 경우 skipna 기능이 없어 산술연산 시 na가 존재하는 경우 이에 대한 전처리가 필요하다.
pandas의 경우 skipna 기능이 있으며 True가 기본값으로 설정되어 있기 때문에 산술연산 시 자동으로 NA를 제외한 결과가 리턴된다.
03 결측치
-01 표현방법
np.nan # 파이썬에서는 np.nan이 기본적으로 float type이다.
from numpy import nan as NA
-02 확인
np.isnan([1,2,3,np.nan]) # numpy용 함수
a2.isnan() # 메서드가 존재하지 않는다.
a3 = Series(['a','b','d',NA]) # 03-01의 NA 선언 필수
np.isnan(a3) # error (np.isnan 함수는 NA가 str 타입일 때는 체크 불가)
np.isnan(a3) # error(np.isnan 함수는 NA가 str 타입일 때는 체크 불가)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In[271], line 1
----> 1 np.isnan(a3) # error(np.isnan 함수는 NA가 str 타입일 때는 체크 불가)
File ~\anaconda3\Lib\site-packages\pandas\core\generic.py:2193, in NDFrame.__array_ufunc__(self, ufunc, method, *inputs, **kwargs)
2189 @final
2190 def __array_ufunc__(
2191 self, ufunc: np.ufunc, method: str, *inputs: Any, **kwargs: Any
2192 ):
-> 2193 return arraylike.array_ufunc(self, ufunc, method, *inputs, **kwargs)
File ~\anaconda3\Lib\site-packages\pandas\core\arraylike.py:399, in array_ufunc(self, ufunc, method, *inputs, **kwargs)
396 elif self.ndim == 1:
397 # ufunc(series, ...)
398 inputs = tuple(extract_array(x, extract_numpy=True) for x in inputs)
--> 399 result = getattr(ufunc, method)(*inputs, **kwargs)
400 else:
401 # ufunc(dataframe)
402 if method == "__call__" and not kwargs:
403 # for np.<ufunc>(..) calls
404 # kwargs cannot necessarily be handled block-by-block, so only
405 # take this path if there are no kwargs
TypeError: ufunc 'isnan' not supported for the input types, and the inputs could not be safely coerced to any supported types according to the casting rule ''safe''
pd.isnull([1,2,3,np.nan]) # pandas용 함수 (상수의 NA 여부 확인 시에는 메서드가 아닌 함수를 사용해야 함)
s2.isnull() # 메서드가 존재한다. (데이터 전체의 NA 여부 확인 시에 메서드 호출)
pd.isnull(a3) # 문자형 NA 체크 가능
pd.isnull(a3) # 문자형 NA 체크 가능
Out[272]:
0 False
1 False
2 False
3 True
dtype: bool
-03 수정
1) 직접수정
s2[s2.isnull()] = 10
2) np.where
s2.isnull 자체가 NA인지 아닌지를 출력하기 때문에 아래와 같은 형식의 코드를 작성할 필요가 없다.
np.where(s2.isnull() == np.nan)
s3 = Series([1,2,3,4,np.nan])
s3 = Series(np.where(s3.isnull(), 10, s3))
s3
Out[251]:
0 1.0
1 2.0
2 3.0
3 4.0
4 10.0
dtype: float64
3) NA 치환 메서드
s3.fillna?
Signature:
s3.fillna(
value: 'Hashable | Mapping | Series | DataFrame | None' = None,
*,
method: 'FillnaOptions | None' = None,
axis: 'Axis | None' = None,
inplace: 'bool_t' = False,
limit: 'int | None' = None,
downcast: 'dict | None | lib.NoDefault' = <no_default>,
) -> 'Self | None'
Docstring:
Fill NA/NaN values using the specified method.
s3 = Series([1,2,3,4,np.nan])
s3.fillna(10, inplace = True) # 원본 테이블에 바로 반영된다.
s3
Out[255]:
0 1.0
1 2.0
2 3.0
3 4.0
4 10.0
dtype: float64
ex) emp에서
1) 각 컬럼별 결측치 수 확인
2) COMM 컬럼의 결측치를 중앙값으로 대치 후 평균을 소숫점 둘째자리까지 반올림하여 출력
emp = pd.read_csv('emp.csv')
emp.head()
# python은 결측치 자체가 type을 가진다. (np.nan이 기본적으로 float type)
# 따라서 emp에서 MGR, COMM이 원래 데이터는 정수형이었음에도 불구하고 NaN이 포함되어 있었기 때문에
# float type으로 바뀌어서 조회되었다.
emp.head()
Out[259]:
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
0 7369 SMITH CLERK 7902.0 1980-12-17 0:00 800 NaN 20
1 7499 ALLEN SALESMAN 7698.0 1981-02-20 0:00 1600 300.0 30
2 7521 WARD SALESMAN 7698.0 1982-02-22 0:00 1250 500.0 30
3 7566 JONES MANAGER 7839.0 1981-04-02 0:00 2975 NaN 20
4 7654 MARTIN SALESMAN 7698.0 1981-09-28 0:00 1250 1400.0 30
1) 컬럼별 결측치 확인
# Series에도 DataFrame에도 모두 적용 가능하다.
pd.isnull(emp).sum()
pd.isnull(emp).sum()
Out[241]:
EMPNO 0
ENAME 0
JOB 0
MGR 0
HIREDATE 0
SAL 0
COMM 0
DEPTNO 0
dtype: int64
2) COMM 컬럼 결측치 대치 후 반올림하여 출력
# R에서 실습했던 것처럼 코드를 작성해도 대치는 가능하나 색인을 2번 진행하였기 때문에 warning이 발생한다.
emp['COMM'][emp['COMM'].isnull()] = 10
'copy of a slice from a DataFrame'
원본 데이터에서 슬라이싱한 데이터를 대치하는 것을 Python에서는 차후 막을 것으로 예쌍된다.
emp['COMM'][emp['COMM'].isnull()] = 10
C:\Users\itwill\AppData\Local\Temp\ipykernel_12396\3095320992.py:1: FutureWarning: ChainedAssignmentError: behaviour will change in pandas 3.0!
You are setting values through chained assignment. Currently this works in certain cases, but when using Copy-on-Write (which will become the default behaviour in pandas 3.0) this will never work to update the original DataFrame or Series, because the intermediate object on which we are setting values will behave as a copy.
A typical example is when you are setting values in a column of a DataFrame, like:
df["col"][row_indexer] = value
Use `df.loc[row_indexer, "col"] = values` instead, to perform the assignment in a single step and ensure this keeps updating the original `df`.
See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
emp['COMM'][emp['COMM'].isnull()] = 10
C:\Users\itwill\AppData\Local\Temp\ipykernel_12396\3095320992.py:1: SettingWithCopyWarning:
A value is trying to be set on a copy of a slice from a DataFrame
See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
emp['COMM'][emp['COMM'].isnull()] = 10
# 인덱싱은 한 번만 진행하고 대치하는 것이 좋다.
emp.loc[emp['COMM'].isnull(), 'COMM'] = emp['COMM'].median()
emp['COMM'].mean().round(2)
emp['COMM'].mean().round(2)
Out[246]: np.float64(442.86)
-04 결측치의 처리
1) 삭제
- NA를 포함한 행 삭제
- NA가 n개 이상 포함된 행, 컬럼 삭제
df = DataFrame([[NA,1,2,3,4],
[NA,NA,3,4,5],
[NA,NA,NA,4,5],
[NA,NA,NA,NA,5],
[NA,NA,NA,NA,NA],
[1,2,3,4,5]], columns=list('ABCDE'))
df
Out[275]:
A B C D E
0 NaN 1.0 2.0 3.0 4.0
1 NaN NaN 3.0 4.0 5.0
2 NaN NaN NaN 4.0 5.0
3 NaN NaN NaN NaN 5.0
4 NaN NaN NaN NaN NaN
5 1.0 2.0 3.0 4.0 5.0
df.dropna?
Signature:
df.dropna(
*,
axis: 'Axis' = 0,
how: 'AnyAll | lib.NoDefault' = <no_default>,
thresh: 'int | lib.NoDefault' = <no_default>,
subset: 'IndexLabel | None' = None,
inplace: 'bool' = False,
ignore_index: 'bool' = False,
) -> 'DataFrame | None'
Docstring:
Remove missing values.
ex) NA가 포함된 행 제거
df.dropna() # 각 행마다 NA가 하나라도 포함되어 있으면 행을 제거
df.dropna() # 각 행마다 NA가 하나라도 포함되어 있으면 행을 제거
Out[278]:
A B C D E
5 1.0 2.0 3.0 4.0 5.0
df.dropna(how='all') # 행의 모든 값이 NA인 행만 제거
df.dropna(how='all')
Out[279]:
A B C D E
0 NaN 1.0 2.0 3.0 4.0
1 NaN NaN 3.0 4.0 5.0
2 NaN NaN NaN 4.0 5.0
3 NaN NaN NaN NaN 5.0
5 1.0 2.0 3.0 4.0 5.0
df.dropna(subset=['E']) # E라는 컬럼에 NA값이 하나라도 포함된다면 행을 제거
df.dropna(subset=['E'])
Out[280]:
A B C D E
0 NaN 1.0 2.0 3.0 4.0
1 NaN NaN 3.0 4.0 5.0
2 NaN NaN NaN 4.0 5.0
3 NaN NaN NaN NaN 5.0
5 1.0 2.0 3.0 4.0 5.0
ex) NA가 포함된 컬럼 제거
df.dropna(axis=1) # 모든 컬럼이 삭제되었음
df.dropna(axis=1)
Out[281]:
Empty DataFrame
Columns: []
Index: [0, 1, 2, 3, 4, 5]
ex) 횟수 기반 삭제
df.dropna(thresh=4) # 각 행별로(axis=0) NA가 아닌 값의 수가 4 미만일 때 삭제 (4 이상인 경우만 남음)
NA 개수가 4개인 것이 아니라 NA가 아닌 값의 개수가 기준이다.
df.dropna(thresh=4)
Out[283]:
A B C D E
0 NaN 1.0 2.0 3.0 4.0
5 1.0 2.0 3.0 4.0 5.0
df.isnull().sum(axis=1) # 행별로 NA 개수 구하기
df.isnull().sum(axis=1)
Out[284]:
0 1
1 2
2 3
3 4
4 5
5 0
dtype: int64
~(df.isnull().sum(axis=1) > 4) # NOT 연산자는 ~
df.loc[~(df.isnull().sum(axis=1) >= 4), :] # 각 행별로(axis=0) NA가 4회 이상 포함된 행 삭제
df.loc[~(df.isnull().sum(axis=1) >= 4), :]
Out[288]:
A B C D E
0 NaN 1.0 2.0 3.0 4.0
1 NaN NaN 3.0 4.0 5.0
2 NaN NaN NaN 4.0 5.0
5 1.0 2.0 3.0 4.0 5.0
ex) NA가 5회 이상 포함된 컬럼 삭제
df.loc[:,~(df.isnull().sum(axis=0) >= 5)]
df.loc[:,~(df.isnull().sum(axis=0) >= 5)]
Out[300]:
B C D E
0 1.0 2.0 3.0 4.0
1 NaN 3.0 4.0 5.0
2 NaN NaN 4.0 5.0
3 NaN NaN NaN 5.0
4 NaN NaN NaN NaN
5 2.0 3.0 4.0 5.0
2) 대치
df.fillna(value, # 대체값 (상수, 딕셔너리, Series, DataFrame)
method =, # {'backfill', 'bfill', 'ffill'}
axis = 0) # 대치방향
df = DataFrame({'A':[10,20,30,NA], 'B':[NA,200,300,400], 'C':[1,2,3,4]})
df
Out[302]:
A B C
0 10.0 NaN 1
1 20.0 200.0 2
2 30.0 300.0 3
3 NaN 400.0 4
ex) 상수로 대치
df.fillna(100)
df.fillna(100)
Out[303]:
A B C
0 10.0 100.0 1
1 20.0 200.0 2
2 30.0 300.0 3
3 100.0 400.0 4
ex) 각 컬럼별로 서로 다른 값으로 대치
df.fillna({'A':40,'B':100}) # 각 컬럼에 어울리는 값을 전달한다.
df.fillna({'A':40,'B':100})
Out[305]:
A B C
0 10.0 100.0 1
1 20.0 200.0 2
2 30.0 300.0 3
3 40.0 400.0 4
ex) 이전값 / 이후값 대치
df['A'].fillna(method='ffill') # 이전값으로 대치
df['A'].ffill() # 이전값으로 대치
df['B'].bfill() # 이후값으로 대치
df['A'].fillna(method='ffill') # 이전값으로 대치
C:\Users\itwill\AppData\Local\Temp\ipykernel_12396\4026065074.py:1: FutureWarning: Series.fillna with 'method' is deprecated and will raise in a future version. Use obj.ffill() or obj.bfill() instead.
df['A'].fillna(method='ffill') # 이전값으로 대치
Out[310]:
0 10.0
1 20.0
2 30.0
3 30.0
Name: A, dtype: float64
df['A'].ffill() # 이전값으로 대치
Out[311]:
0 10.0
1 20.0
2 30.0
3 30.0
Name: A, dtype: float64
df['B'].bfill() # 이후값으로 대치
Out[312]:
0 200.0
1 200.0
2 300.0
3 400.0
Name: B, dtype: float64
[ 연습문제 ]
sub = pd.read_csv('subway2.csv', encoding='cp949', skiprows = 1)
sub.head()
(1) 역이름 모두 채우기
sub['전체'] = sub['전체'].ffill()
sub
Out[349]:
전체 구분 05~06 06~07 07~08 ... 20~21 21~22 22~23 23~24 24~01
0 서울역(1) 승차 17465 18434 50313 ... 108909 116350 88902 49049 4558
1 서울역(1) 하차 7829 48553 110250 ... 65388 59285 50266 32182 13943
2 시 청(1) 승차 2993 4473 7633 ... 74034 85680 69327 24653 1691
3 시 청(1) 하차 4142 19730 67995 ... 25171 14515 8552 5349 1603
4 종 각 승차 7371 7836 14545 ... 138406 176800 182774 86306 6074
.. ... .. ... ... ... ... ... ... ... ... ...
227 총신대 하차 1094 13095 26021 ... 63246 56352 57793 37546 10558
228 사당(4) 승차 12640 32077 90179 ... 44821 45749 48776 25147 3197
229 사당(4) 하차 2435 20684 39923 ... 46874 45369 47597 34915 16260
230 남태령 승차 388 878 2127 ... 1046 806 539 227 51
231 남태령 하차 77 1384 3142 ... 1374 1460 1515 1014 387
(2) 역별, 승하차별 인원수 총합 구하기
sub.iloc[:,2:].sum(axis=1) # Series로 출력 (1차원)
sub.iloc[:,2:].sum(axis=1).values.reshape(-1,1) # values로 값만 꺼내서 reshape을 토대로 2차원 구조를 만든다.
sub2 = DataFrame(sub.iloc[:,2:].sum(axis=1).values.reshape(-1,1),columns = ['총합']) # DataFrame으로 출력 (★)
pd.concat([sub.iloc[:,:2], sub2], axis=1)
pd.concat([sub.iloc[:,:2], sub2], axis=1)
Out[360]:
전체 구분 총합
0 서울역(1) 승차 1830134
1 서울역(1) 하차 1565575
2 시 청(1) 승차 808764
3 시 청(1) 하차 835631
4 종 각 승차 1604404
.. ... .. ...
227 총신대 하차 902911
228 사당(4) 승차 1072854
229 사당(4) 하차 791552
230 남태령 승차 35418
231 남태령 하차 36025
[232 rows x 3 columns]
ex) 특정 Series의 값으로 대치 (★ 매우 중요)
df
다른 컬럼의 같은 위치에 있는 값으로 대치한다.
즉, A의 NA는 C의 4로 B의 NA는 C의 1로 대치된다.
df
Out[361]:
A B C
0 10.0 NaN 1
1 20.0 200.0 2
2 30.0 300.0 3
3 NaN 400.0 4
df['A'].fillna(df['C'])
df['A'].fillna(df['C'])
Out[362]:
0 10.0
1 20.0
2 30.0
3 4.0
Name: A, dtype: float64
[ 연습문제 ]
df = pd.read_csv('boston_na.csv')
# 결측치 확인하기
1) .info()
df.info()
506 entries이므로 non-null count가 506이 아닌 컬럼은 NA를 포함하고 있음을 뜻한다.
df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 506 entries, 0 to 505
Data columns (total 14 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 crim 506 non-null float64
1 zn 506 non-null float64
2 indus 506 non-null float64
3 chas 506 non-null int64
4 nox 506 non-null float64
5 rm 494 non-null object
6 age 506 non-null float64
7 dis 501 non-null object
8 rad 506 non-null int64
9 tax 506 non-null int64
10 ptratio 506 non-null float64
11 black 506 non-null float64
12 lstat 506 non-null float64
13 medv 506 non-null float64
dtypes: float64(9), int64(3), object(2)
memory usage: 55.5+ KB
2) .isnull().sum()
각 컬럼별 결측치 수를 확인한다.
df.isnull().sum()
df.isnull().sum()
Out[366]:
crim 0
zn 0
indus 0
chas 0
nox 0
rm 12
age 0
dis 5
rad 0
tax 0
ptratio 0
black 0
lstat 0
medv 0
dtype: int64
3) .isnull().any(axis=0)
각 컬럼별 결측치 포함 여부를 확인한다.
df.isnull().any(axis=0)
df.isnull().any(axis=0)
Out[367]:
crim False
zn False
indus False
chas False
nox False
rm True
age False
dis True
rad False
tax False
ptratio False
black False
lstat False
medv False
dtype: bool
2) 삭제
ex) 특정 Series의 값으로 대치
df['A'].fillna(df['C'])
[ 연습문제 ]
df = pd.read_csv('boston_na.csv')
df.info()
df.isnull().sum()
df.isnull().any(axis=0)
pd.set_option('display.max_columns', None)
df.head()
# 1) dis 컬럼에 대해 결측치를 전체 평균으로 대치 후 중앙값을 소수점 셋째자리까지 반올림하여 출력
df.loc[df['dis'].isin(['-', '.']), 'dis'] = np.nan
df['dis'] = df['dis'].astype('float')
df['dis'] = df['dis'].fillna(df['dis'].mean())
round(df['dis'].median(),3)
df.loc[df['dis'].isin(['-', '.']), 'dis'] = np.nan
df['dis'] = df['dis'].astype('float')
df['dis'] = df['dis'].fillna(df['dis'].mean())
round(df['dis'].median(),3)
Out[498]: 3.299
# 문제풀이
df['dis'].astype('float') # '-' 때문에 변경 불가
df.loc[df['dis'] == '-', 'dis'] = NA # - -> NA로 변경
df['dis'].astype('float') # '.' 때문에 변경 불가
df.loc[df['dis'] == '.', 'dis'] = NA # . -> NA로 변경
df['dis'] = df['dis'].astype('float') # 데이터타입 변환
df['dis'] = df['dis'].fillna(df['dis'].mean()) # 평균으로 대치
round(df['dis'].median(), 3)
df['dis'].astype('float') # '-' 때문에 변경 불가
df.loc[df['dis'] == '-', 'dis'] = NA # - -> NA로 변경
df['dis'].astype('float') # '.' 때문에 변경 불가
df.loc[df['dis'] == '.', 'dis'] = NA # . -> NA로 변경
df['dis'] = df['dis'].astype('float') # 데이터타입 변환
df['dis'] = df['dis'].fillna(df['dis'].mean()) # 평균으로 대치
round(df['dis'].median(), 3)
Out[499]: 3.299
# 2) rm 컬럼에 대해 chas별 평균으로 결측치 대치 후 분산을 소숫점 셋째자리까지 반올림하여 출력
# 문제풀이
df['rm'].astype('float') # ' ' 때문에 변경 불가
df.loc[df['rm'] == ' ', : ] = NA # 공백 제거 후 NA로 변경
df['rm'] = df['rm'].astype('float') # 데이터타입 변환
rm_mean = df.groupby('chas')['rm'].transform('mean') # chas별 평균 구하기
df['rm'] = df['rm'].fillna(rm_mean)
round(df['rm'].var(),3)
'아이티윌_데이터 분석 55기 > 강의내용 필기_Python' 카테고리의 다른 글
| #8 8일차_빈도수 / 유일값 확인, 중복값 처리, shift, multi-level index (0) | 2026.05.20 |
|---|---|
| #7 7일차_논리연산자/포함연산자, pandas index 설정, pandas 정렬, 벡터화 내장된 문자열 메서드, idxmax / idxmin (0) | 2026.05.19 |
| #5 5일차_형 변환 함수/매서드, SQL사용, DB 연동, array (0) | 2026.05.15 |
| #4 4일차_사용자 정의함수, groupby 연산, 조인 (0) | 2026.05.14 |
| #3 3일차_조건문, 반복문 (0) | 2026.05.13 |