1. 데이터 로드
import pandas as pd
train = pd.read_csv('train.csv', parse_dates=['일시'])
# '일시'를 인덱스로 설정
train = train.set_index('일시')
display(train.head(2))
2. train 데이터 결측값 정보 확인
train.isnull().sum()
최고기온 3
최저기온 3
일교차 4
강수량 13861
평균습도 0
평균풍속 4
일조합 118
일사합 4862
일조율 366
평균기온 0
dtype: int64
3. 결측값이 존재하는 일교차, 최고기온, 최저기온 확인
일교차는 최고기온과 최저기온의 차이로 정의됩니다.
데이터셋에서 일교차, 최고기온, 또는 최저기온 중 하나가 결측값인 경우, 다음과 같은 방법으로 이를 대체할 수 있습니다.일교차 결측값: 최고기온과 최저기온이 모두 기록되어 있다면, 일교차는 최고기온에서 최저기온을 뺀 값으로 채울 수 있습니다.
최고기온 결측값: 일교차와 최저기온이 기록되어 있다면, 최고기온은 최저기온에 일교차를 더한 값으로 채울 수 있습니다.최저기온 결측값: 일교차와 최고기온이 기록되어 있다면, 최저기온은 최고기온에서 일교차를 뺀 값으로 채울 수 있습니다.
이 방법들은 각 변수에서 단 하나의 값만 결측일 경우에 적용할 수 있으며, 두 개 이상의 값이 결측일 경우 다른 방법을 고려해야 합니다.
※ isna()
isna() 메서드는 데이터프레임(DataFrame) 또는 시리즈(Series)의 각 요소에 대해 결측값 여부를 확인할 때 사용됩니다.
- 결측값이면 True 반환
- 결측값이 아니면 False 반환
condition1 = train['최고기온'].isna()
condition2 = train['최저기온'].isna()
condition3 = train['일교차'].isna()
train[condition1 | condition2 | condition3]
※ 결과 해석
세 조건을 만족시키는 데이터프레임을 출력하였을 때 모든 행에 대해 두 개 이상의 결측값이 관측되었습니다.결측값이 있는 날짜에서, 만약 해당 날짜의 앞뒤 데이터가 존재한다면, 이를 활용해 선형 보간법(linear interpolation)을 적용하여 결측값을 채울 수 있습니다.선형 보간법은 결측값이 앞뒤 값을 사용하여 선형적으로 채우는 방법으로, 이전과 이후의 값이 비교적 안정적인 패턴을 보일 때 유용합니다.
결측값을 제거하지 않는 이유는 시계열 데이터의 경우, 각 시점의 데이터 그 자체로서 시간적 연속성의 패턴을 나타내기 때문입니다.결측 데이터를 임의로 제거한다면, 시간에 따라 데이터의 흐름과 패턴이 왜곡될 수 있고, 이는 분석 결과에 큰 오류를 야기할 수 있습니다.
특히, 시계열 분석에 있어서는 데이터의 연속성이 중대한 역할을 하므로, 결측값을 제거하는 것보다는 대신 적절한 방법으로 이를 채우는 것이 더 바람직합니다.
4. 일교차, 최고기온, 최저기온 결측값 대체
※ 선형 보간법
선형 보간법 선형 보간법은 결측값이 있는 데이터 포인트를 대체하기 위해 사용되는 방법입니다.
이 방법은 결측값 앞뒤의 데이터 포인트를 사용하여 직선적인 관계를 가정하고, 그 직선 위의 점을 결측값으로 간주하여 값을 추정합니다.
특정 시점의 데이터가 누락된 경우 데이터 바로 앞과 뒤의 데이터를 사용하여 직선을 그리고, 그 직선 상에 누락된 시간을 추정합니다.이 방법은 데이터 포인트 사이의 관계가 선형적으로 변한다고 가정할 때 유용하며, 데이터가 비교적 균등한 간격으로 정렬되고, 패턴이 일정할 때 효과적입니다.
선형 보간법은 앞뒤 데이터를 연결하는 방식이기 때문에 이번 데이터와 같은 시계열 데이터에서는 특히 유용합니다.
시계열 데이터는 시간에 따른 일정한 패턴이나 경향성을 가지고 있는 경우가 많으며, 선형 보간법은 이러한 경향을 반영하여 결측값을 채웁니다.
# 최고기온과 최저기온의 결측값을 선형 보간법으로 채우기
train['최고기온'] = train['최고기온'].interpolate(method='linear')
train['최저기온'] = train['최저기온'].interpolate(method='linear')
# '일교차'가 결측인 경우에 한하여 최고기온과 최저기온의 차로 '일교차' 계산
train.loc[train['일교차'].isna(), '일교차'] = train['최고기온'] - train['최저기온']
# 결측값 대체 확인
display(train[condition1 | condition2 | condition3])
print(train.isnull().sum())
최고기온 0
최저기온 0
일교차 0
강수량 13861
평균습도 0
평균풍속 4
일조합 118
일사합 4862
일조율 366
평균기온 0
dtype: int64
5. 일조합, 일사합, 일조율 칼럼의 상관관계 확인
import seaborn as sns
sun_data = train[['일조합', '일사합', '일조율']]
fig, ax = plt.subplots()
sun_corr = sun_data.corr()
sns.heatmap(sun_corr, annot=True, ax=ax)
plt.show()
※ 결과 해석
일조합, 일조율 두 칼럼의 상관관계가 0.97로 매우 높게 나온 것을 확인할 수 있습니다.
위 두 칼럼보다는 낮지만, 일사합, 일조합 칼럼의 상관관계도 0.79 정도로 높은 것으로 보입니다.
따라서, 일조율, 일조합 칼럼의 결측값을 대체한 이후 일사합 칼럼의 결측값을 대체하면 될 것으로 보입니다.
상관관계가 높다는 것은 두 변수가 서로 연관성이 있음을 의미하지만, 이는 반드시 인과관계를 의미하지는 않습니다.
따라서 데이터를 분석할 때 해석 사항을 고려하여야 할 것입니다.
6. 일조합, 일조율 값이 결측값인 항목 식별
상관관계가 높은 두 변수 일조합과 일조율을 사용하여 결측값을 대체할 계획을 세울 때, 중요한 전체 조건은 두 변수 중 적어도 하나에 유효한 데이터가 존재해야 한다는 것입니다.
만약 어떤 데이터 포인트가 두 변수 모두에서 결측값을 가지고 있다면, 이는 우리가 가진 정보로는 대체할 수 없기 때문입니다. 이러한 행이 존재한다면, 이들에 대해서 선형 회귀나 다른 변수 기반의 대체 방법을 사용할 수 없습니다.
두 변수가 서로 상관관계에 기반하는 방법을 사용할 때, 적어도 하나의 변수에는 정보가 있어야 하기 때문입니다. 이러한 행이 실제로 존재한다면, 다른 대체 방법을 찾아야 합니다.
train[(train['일조합'].insa()) & (train['일조율'].isna())]
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
/tmp/ipykernel_392/4238706840.py in ?()
----> 1 train[(train['일조합'].insa()) & (train['일조율'].isna())]
/usr/local/lib/python3.11/site-packages/pandas/core/generic.py in ?(self, name)
5898 and name not in self._accessors
5899 and self._info_axis._can_hold_identifiers_and_holds_name(name)
5900 ):
5901 return self[name]
-> 5902 return object.__getattribute__(self, name)
AttributeError: 'Series' object has no attribute 'insa'
※ 결과 해석
일조합과 일조율 값이 모두 결측값인 데이터는 존재하지 않으므로,
각 변수를 이용해서 결측값을 대체하는 것이 가능합니다.
7. 선형 회귀를 이용한 일조율, 일조합 값 대체
일조합과 일조율 간의 상관관계가 매우 높은 0.97로 나타났습니다.
이는 높은 상관관계는 두 변수 사이에 강한 선형 관계가 있음을 나타냅니다.
따라서 일조합과 일조율은 서로 밀접하게 연관되어 있으며, 한 변수의 변화가 다른 변수에 매우 높은 확률로 영향을 미칩니다.
이러한 상황에서, 우리는 두 가지 주요 이유로 일조합과 일조율을 상호 보완하여 결측값을 예측하고자 합니다.
두 변수 간의 높은 상관관계를 활용하면 하나의 변수를 사용하여 다른 변수의 결측값을 예측하는 데 정확한 정보를 얻을 수 있습니다.
결측값이 있는 데이터를 제거하거나 다른 방법으로 처리하는 대신,
이러한 관련성을 활용하여 데이터의 대부분을 유지하고 결측값을 보다 정확하게 대체할 수 있습니다.
※ notna()
notna() 메서드는 데이터프레임 또는 시리즈의 각 요소에 대해 결측값 여부를 확인합니다.
- 결측값이 아닌 요소에 대해서는 True를 반환하고,
- 결측값인 경우 False를 반환합니다.
from sklearn.linear_model import LinearRegression
# 일조율 결측값을 채우기 위한 모델 정의
fill_rate_model = LinearRegression()
# 결측값이 없는 데이터로 모델을 학습
not_null_data = train[(train['일조합'].notna()) & (train['일조율'].notna())]
# 독립변수로 '일조합'을 사용
X = not_null_data[['일조합']]
y = not_null_data['일조율']
fill_rate_model.fit(X, y)
# 일조율 결측값을 예측하여 채우기
is_null_일조율 = train['일조율'].isna()
train.loc[is_null_일조율, '일조율'] = fill_rate_model.predict(train.loc[is_null_일조율, ['일조합']])
# 독립변수로 '일조율'을 사용
X = train[['일조율']]
y = train['일조합'].dropna()
# 일조합 결측값을 채우기 위한 모델 정의
fill_sum_model = LinearRegression()
fill_sum_model.fit(X[~train['일조합'].isna()], y)
# 일조합 결측값을 예측하여 채우기
is_null_일조합 = train['일조합'].isna()
train.loc[is_null_일조합, '일조합'] = fill_sum_model.predict(train.loc[is_null_일조합, ['일조율']])
# 모든 결측값이 채워졌는지 확인
train.isnull().sum()
※ 코드 설명
- LinearRegression 모델을 사용하여 일조율 의 결측값을 예측하기 위한 모델을 정의합니다.
- notna() 메서드를 사용하여 일조합 과 일조율 모두 결측값이 없는 데이터를 필터링하여 not_null_data 변수에 저장합니다.
- 일조합 을 독립변수(X)로, 일조율 을 종속변수(y)로 설정하고, 선형회귀 모델을 이 데이터를 사용하여 학습합니다.
- 일조율 이 결측값인 행을 찾아, 학습된 모델을 사용하여 일조율 의 결측값을 예측하고 채웁니다.
- 일조율 을 새로운 독립변수(X)로, 일조합 을 새로운 종속변수(y)로 설정합니다.
- 새롭게 정의된 모델을 학습하고, 이를 사용하여 일조합 의 결측값을 예측하고 채웁니다.
- 마지막으로, 일조율 과 일조합 의 결측값을 확인하여 모든 결측값이 채워졌는지 확인합니다.
최고기온 0
최저기온 0
일교차 0
강수량 13861
평균습도 0
평균풍속 4
일조합 0
일사합 4862
일조율 0
평균기온 0
dtype: int64
※ 결과 해석
일조합, 일조율 칼럼의 모든 결측값이 대체되었습니다. 이를 바탕으로 일사합 칼럼의 값을 대체할 수 있습니다.
8. 선형 회귀를 이용한 일사합 결측값 대체
# 일사합 결측값을 채우기 위한 모델 정의
fill_irradiance_model = LinearRegression()
# 결측값이 없는 데이터로 모델을 학습
not_null_irradiance_data = train[(train['일조합'].notna()) & (train['일사합'].notna())]
# 독립변수로 '일조합'을 사용
X_irradiance = not_null_irradiance_data[['일조합']]
y_irradiance = not_null_irradiance_data['일사합']
fill_irradiance_model.fit(X_irradiance, y_irradiance)
# 일사합 결측값을 예측하여 채우기
is_null_일사합 = train['일사합'].isna()
train.loc[is_null_일사합, '일사합'] = fill_irradiance_model.predict(train.loc[is_null_일사합, ['일조합']])
# 모든 결측값이 채워졌는지 확인
train.isnull().sum()
※ 코드 설명
- 선형회귀 모델을 정의하고 fill_irradiance_model 변수에 저장합니다.
- 일조합, 일사합 의 값이 모두 결측값이 아닌 데이터만을 선택하여 not_null_irradiance_data 변수에 저장합니다.
- 독립변수에 일조합 칼럼을, 종속 변수에 일사합 칼럼을 지정하고, 선형회귀 모델을 학습합니다.
- 일사합 의 값이 결측값인 데이터를 is_null_일사합 변수에 저장합니다.
- 선형회귀의 예측값으로 일사합 의 값을 채웁니다.
최고기온 0
최저기온 0
일교차 0
강수량 13861
평균습도 0
평균풍속 4
일조합 0
일사합 0
일조율 0
평균기온 0
dtype: int64
※ 결과 해석
상관관계 분석 결과, 일조합과 일사합 간에는 0.79의 상관계수가 나타났습니다.
이는 일조합과 일조율 사이의 0.97 상관계수보다는 낮지만, 여전히 0.6 이상이므로 두 변수 사이에는 강한 선형 관계가 존재한다고 볼 수 있습니다.
이러한 상관관계를 바탕으로 일조합 값을 선형 회귀 모델의 입력으로 사용하면, 일사합의 결측값을 효과적으로 예측하고 대체할 수 있을 것으로 기대됩니다.
9. 중앙값을 활용한 평균풍속 결측값 대체
위에서 확인했을 때 평균풍속의 결측값은 4개로 판명되었습니다.
평균풍속의 결측값을 해당 월의 중앙값으로 대체하겠습니다.
평균풍속의 결측값을 대체할 때 해당 월의 중앙값을 사용하는 것이 평균값을 사용하는 것에 비해 여러 이점이 있습니다.
먼저, 중앙값은 이상치의 영향을 덜 받습니다.
풍속 데이터는 강한 바람이나 기상 이변으로 인해 일시적으로 극단적인 값을 보일 수 있습니다.
이러한 이상치들은 평균값을 왜곡할 수 있지만, 중앙값은 이러한 극단적인 값들에 덜 영향을 받기 때문에 더 안정적입니다.
또한, 월별 중앙값을 사용하는 것은 계절적 변화를 고려하는 효과적인 방법입니다.
각 월마다 풍속의 특징이 다를 수 있으며, 월별 중앙값을 사용하면 해당 계절적 패턴을 반영할 수 있습니다.
예를 들어, 겨울에는 일반적으로 풍속이 강하고 여름에는 약할 수 있습니다.
하지만 계절 대신 월별 중앙값을 사용하면 한 기간의 일반적인 특성을 더 잘 반영할 수 있습니다.
# 일자에서 월 추출
train['월'] = train.index.month
# 각 월별 평균풍속의 중앙값 계산
median_wind_speed_per_month = train.groupby('월')['평균풍속'].median()
# 각 월별로 평균풍속 결측값을 해당 월의 중앙값으로 대체
for month, median in median_wind_speed_per_month.items():
train.loc[(train['월'] == month) & (train['평균풍속'].isna()), '평균풍속'] = median
# 더 이상 필요하지 않은 '월' 컬럼 삭제
train.drop('월', axis=1, inplace=True)
# 결측값이 잘 대체되었는지 확인
train.isnull().sum()
※ 코드 설명
- rain 데이터프레임의 일자 인덱스에서 month 속성을 사용하여 월을 추출하고 월 칼럼을 생성합니다.
- median() 메서드를 사용하여 각 월별로 평균풍속 의 중앙값을 계산합니다.
- 계산된 중앙값을 사용하여 각 월별 평균풍속 결측값을 해당 월의 중앙값으로 대체합니다.
- 대체가 완료된 후 월 칼럼을 삭제합니다.
최고기온 0
최저기온 0
일교차 0
강수량 13861
평균습도 0
평균풍속 0
일조합 0
일사합 0
일조율 0
평균기온 0
dtype: int64
10. 강수량 결측값 대체
전체 데이터의 개수는 23,011개이며, 강수량 칼럼에서 결측값의 수는 13,861개입니다.
이는 전체 데이터 중 약 60%에 해당하는 상당한 비율입니다.
이렇게 높은 비율의 결측값을 적절히 처리함으로써 데이터의 연속성을 유지하고 실질적인 정보의 손실을 최소화할 수 있습니다.
fillna() 메서드의 bfill 방법을 사용하면 결측값 발생 직후의 값을 사용하여 결측값을 대체할 수 있습니다.

fillna() 메서드는 결측값(NaN)을 다른 값으로 채우는 데 사용됩니다.
주요 매개변수는 다음과 같습니다.
- value : 결측값을 대체할 값을 지정할 수 있습니다. 예를 들어 모든 결측값을 0.0으로 대체하고 싶다면 value=0을 설정할 수 있습니다.
- method : 결측값을 어떻게 채울지 결정합니다. 아래의 코드에서 사용한 bfill은 결측값 다음에 있는 유효한 값으로 결측값을 채웁니다.
기상 자료는 인접한 시간대에서 유사한 경향을 보이는 경우가 많으므로,
bfill 방법은 실제 기상 상황을 잘 반영할 수 있습니다.
또한, 평균, 중앙값 또는 최근값과 같은 다른 대체 방법들은 전체 데이터의 분포에 영향을 줄 수 있는 반면, bfill은 인접한 실제 값 기반이기 때문에 전체 데이터의 왜곡을 최소화할 수 있습니다.
train['강수량'].fillna(method='bfill', inplace = True)
train.isnull().sum()
최고기온 0
최저기온 0
일교차 0
강수량 0
평균습도 0
평균풍속 0
일조합 0
일사합 0
일조율 0
평균기온 0
dtype: int64
11. 데이터 저장
일반적으로 인덱스의 정보는 필요 없기 때문에 index 파라미터의 값을 False로 설정하여 인덱스 정보를 제거합니다.
그러나, 우리는 일자 칼럼을 인덱스로 지정하였습니다.
index = False로 설정할 경우 일자 데이터가 모두 사라질 수 있기 때문에 이를 보존하기 위해 index = True로 설정합니다.
train.to_csv('preprocessing_train.csv', index = True)
'딥러닝 > 딥러닝: 실전 프로젝트 학습' 카테고리의 다른 글
시계열 : 서울시 평균기온 예측 프로젝트 6(다변량 시계열 예측 모델 구축) (0) | 2025.02.07 |
---|---|
시계열 : 서울시 평균기온 예측 프로젝트 5(전처리) (1) | 2025.02.07 |
시계열 : 서울시 평균기온 예측 프로젝트 3(ARIMA 모델을 사용한 단변량 예측) (0) | 2025.02.04 |
시계열 : 서울시 평균기온 예측 프로젝트 2(시각화 분석) (0) | 2025.02.03 |
시계열 : 서울시 평균기온 예측 프로젝트 1(데이터 이해) (1) | 2025.02.02 |