카테고리 없음
36. TIL) 스마트 팜 팀 프로젝트(5) - 추후 수정 추가 예정
kaseomi
2025. 2. 10. 19:03
728x90
시계열 데이터를 다루는 와중 셔플을 false 했어야 했는데 안하고 진행하였음 ,,
너무 늦게 알아버려따 ,,,
다중회귀분석
# 환경 데이터 경량화
# 날짜 변환: 초/분/시 제거하고 'YYYY-MM-DD' 형식으로 변환
env_df['measDate'] = pd.to_datetime(env_df['measDate']).dt.date
# `senVal`을 숫자로 변환 (문자열이 포함된 경우 NaN 처리)
env_df['senVal'] = pd.to_numeric(env_df['senVal'], errors='coerce')
# 온실번호 목록 확보 (모든 온실 유지)
all_farms = ['천지인술', 'Trigger', '이삭줍는 알파고', '토마토명가']
# 모든 온실번호를 유지하면서 환경 변수별 평균값 계산
env_df_grouped = env_df.pivot_table(
values='senVal',
index=['measDate', 'farm_cde'], # 날짜 & 온실번호별 정리
columns='fatrCode', # 환경 변수별 컬럼화
aggfunc='mean' # 평균값 계산
).reset_index()
# 모든 farm_cde(온실번호)가 유지되도록 보정 (없는 경우 0으로 채우기)
final_df_list = []
for farm in all_farms:
temp_df = env_df_grouped.copy()
temp_df['farm_cde'] = farm # 모든 farm_cde를 유지
final_df_list.append(temp_df)
# 모든 온실번호를 포함하는 최종 DataFrame 생성
final_df = pd.concat(final_df_list, ignore_index=True)
final_df = final_df.fillna(0) # 결측치 제거
# 날짜 변환
env_df_grouped['measDate'] = pd.to_datetime(env_df_grouped['measDate']).dt.date
prod_df['measDate'] = pd.to_datetime(prod_df['measDate']).dt.date
# 생산정보에서 필요한 열 선택 (measDate, farm_cde, outtrn)
prod_df_selected = prod_df[['measDate', 'farm_cde', 'outtrn']]
# 환경정보와 생산정보 병합
merged_df = pd.merge(env_df_grouped, prod_df_selected, on=['measDate', 'farm_cde'], how='left')
# 병합된 데이터 개수 확인
print("병합된 데이터 개수:", merged_df.shape)
# 수확량(outtrn) 값이 있는지 확인
print("수확량 결측치 개수:", merged_df['outtrn'].isna().sum())
print("수확량 데이터 샘플:", merged_df[['measDate', 'farm_cde', 'outtrn']].dropna().head())
# 결측치가 많은 변수(EI, EO, PI, PO, PL, EL) 제외
exclude_vars = ['EI', 'EO', 'PI', 'PO', 'PL', 'EL']
selected_columns = [col for col in merged_df.columns if col not in exclude_vars]
# 분석을 위한 데이터 정리 (결측치 제거)
merged_df_cleaned = merged_df[selected_columns].dropna()
# X, y 데이터 확인
if merged_df_cleaned.empty:
print(" 데이터셋이 비어 있어 회귀 분석을 수행할 수 없습니다. 데이터 병합을 확인하세요.")
else:
# 독립변수(X)와 종속변수(y) 설정
X = merged_df_cleaned.drop(columns=['measDate', 'farm_cde', 'outtrn']) # 날짜, 온실번호, 종속변수 제외
y = merged_df_cleaned['outtrn'] # 수확량
# 상수항 추가 (절편 포함한 회귀 분석)
X = sm.add_constant(X)
# 회귀 분석 실행
model = sm.OLS(y, X).fit()
# 회귀 분석 결과 출력
print(model.summary())
# 날짜 변환 (YYYY-MM-DD 형식)
grow_df['measDate'] = pd.to_datetime(grow_df['measDate']).dt.date
# 생육정보에서 주요 변수 선택
grow_cols = ['measDate', 'farm_cde', 'flowerTop', 'grwtLt', 'lefCunt', 'lefLt',
'lefBt', 'stemThck', 'flanGrupp', 'frtstGrupp', 'hvstGrupp', 'frtstCo']
grow_df_selected = grow_df[grow_cols]
# 환경정보 + 생육정보 + 생산정보 (수확량) 병합
merged_df = pd.merge(env_df_grouped, grow_df_selected, on=['measDate', 'farm_cde'], how='left')
merged_df = pd.merge(merged_df, prod_df_selected, on=['measDate', 'farm_cde'], how='left')
# 병합된 데이터 개수 확인
print("병합된 데이터 개수:", merged_df.shape)
# 수확량(outtrn) 값이 있는지 확인
print("수확량 결측치 개수:", merged_df['outtrn'].isna().sum())
print("수확량 데이터 샘플:\n", merged_df[['measDate', 'farm_cde', 'outtrn']].dropna().head())
# 결측치가 많은 변수(EI, EO, PI, PO, PL, EL) 제외
exclude_vars = ['EI', 'EO', 'PI', 'PO', 'PL', 'EL']
selected_columns = [col for col in merged_df.columns if col not in exclude_vars]
# 분석을 위한 데이터 정리 (결측치 제거)
merged_df_cleaned = merged_df[selected_columns].dropna()
# X, y 데이터 확인
if merged_df_cleaned.empty:
print(" 데이터셋이 비어 있어 회귀 분석을 수행할 수 없습니다. 데이터 병합을 확인하세요.")
else:
# 독립변수(X)와 종속변수(y) 설정
X = merged_df_cleaned.drop(columns=['measDate', 'farm_cde', 'outtrn']) # 날짜, 온실번호, 종속변수 제외
y = merged_df_cleaned['outtrn'] # 수확량
# 상수항 추가 (절편 포함한 회귀 분석)
X = sm.add_constant(X)
# 회귀 분석 실행
model = sm.OLS(y, X).fit()
# 회귀 분석 결과 출력
print(model.summary())
# 날짜 변환 (YYYY-MM-DD 형식)
grow_df['measDate'] = pd.to_datetime(grow_df['measDate']).dt.date
# 생육정보에서 주요 변수 선택
grow_cols = ['measDate', 'farm_cde', 'flowerTop', 'grwtLt', 'lefCunt', 'lefLt',
'lefBt', 'stemThck', 'flanGrupp', 'frtstGrupp', 'hvstGrupp', 'frtstCo']
grow_df_selected = grow_df[grow_cols]
# 환경정보 + 생육정보 병합
merged_df = pd.merge(env_df_grouped, grow_df_selected, on=['measDate', 'farm_cde'], how='left')
# 병합된 데이터 개수 확인
print("병합된 데이터 개수:", merged_df.shape)
# 생장길이(grwtLt) 값이 있는지 확인
print("생장길이 결측치 개수:", merged_df['grwtLt'].isna().sum())
print("생장길이 데이터 샘플:\n", merged_df[['measDate', 'farm_cde', 'grwtLt']].dropna().head())
# 결측치가 많은 변수(EI, EO, PI, PO, PL, EL) 제외
exclude_vars = ['EI', 'EO', 'PI', 'PO', 'PL', 'EL']
selected_columns = [col for col in merged_df.columns if col not in exclude_vars]
# 분석을 위한 데이터 정리 (결측치 제거)
merged_df_cleaned = merged_df[selected_columns].dropna()
# X, y 데이터 확인
if merged_df_cleaned.empty:
print(" 데이터셋이 비어 있어 회귀 분석을 수행할 수 없습니다. 데이터 병합을 확인하세요.")
else:
# 독립변수(X)와 종속변수(y) 설정
X = merged_df_cleaned.drop(columns=['measDate', 'farm_cde', 'grwtLt']) # 날짜, 온실번호, 종속변수 제외
y = merged_df_cleaned['grwtLt'] # 생장길이
# 상수항 추가 (절편 포함한 회귀 분석)
X = sm.add_constant(X)
# 회귀 분석 실행
model = sm.OLS(y, X).fit()
# 회귀 분석 결과 출력
print(model.summary())
환경정보와 생육정보 간의 관계
릿지회귀분석(능형회귀분석)
# measDate 포맷 정리
grow_df['measDate'] = pd.to_datetime(grow_df['measDate']).dt.date
env_data['measDate'] = pd.to_datetime(env_data['measDate']).dt.date
# 병합 수행 (공통 데이터만 남김)
merged_data = pd.merge(grow_df, env_data, on=['measDate', 'farm_cde'], how='inner')
# 병합 후 확인
print("Merged Data Sample:\n", merged_data.head())
# 결측치 개수 확인
print("Missing values in merged_data:\n", merged_data.isnull().sum())
# 독립 변수(X): 환경 변수 (온도, 습도, CO₂, 광량 등)
X = merged_data[['TA', 'HI', 'CI', 'IR']]
# 종속 변수(Y): 생육 지표 (줄기 직경, 엽수, 개화 수준 등)
Y_columns = ['stemThck', 'lefCunt', 'flanGrupp', 'frtstGrupp']
Y = merged_data[Y_columns]
# 결측치 확인
if Y.isnull().all().all():
raise ValueError("All target variables (Y) are NaN after merging. Check measDate and farm_cde.")
# KNN Imputer 적용
imputer = KNNImputer(n_neighbors=5)
if not X.empty:
X = pd.DataFrame(imputer.fit_transform(X), columns=X.columns)
if not Y.empty:
Y = pd.DataFrame(imputer.fit_transform(Y), columns=Y.columns)
# 데이터 스케일링
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
# 데이터 분할
X_train, X_test, Y_train, Y_test = train_test_split(X_scaled, Y, test_size=0.2, random_state=42)
# 릿지 회귀 모델 학습
ridge = Ridge(alpha=1.0)
ridge.fit(X_train, Y_train)
# 예측 및 성능 평가
Y_pred = ridge.predict(X_test)
r2_scores_ridge = {col: r2_score(Y_test[col], Y_pred[:, i]) for i, col in enumerate(Y.columns)}
# 결과 출력
r2_scores_df = pd.DataFrame.from_dict(r2_scores_ridge, orient='index', columns=['R2 Score'])
# 결과를 터미널에 출력
print("\n===== Ridge Regression R2 Scores =====")
print(r2_scores_df)
다음에 나오는 랜덤포레스트에 비해 설명력이 다소 모자란 것을 볼 수 있음
데이터가 적기 때문이지 않을까 추측
랜덤포레스트
# 랜덤 포레스트 회귀 모델 학습
rf = RandomForestRegressor(n_estimators=100, random_state=42)
rf.fit(X_train, Y_train)
# 예측 및 성능 평가
Y_pred_rf = rf.predict(X_test)
r2_scores_rf = {col: r2_score(Y_test[col], Y_pred_rf[:, i]) for i, col in enumerate(Y.columns)}
# 결과 출력
r2_scores_rf_df = pd.DataFrame.from_dict(r2_scores_rf, orient='index', columns=['R2 Score'])
print("\n===== Random Forest Regression R2 Scores =====")
print(r2_scores_rf_df)
R 스퀘어값이 비교적 높은 것을 알 수 있음
데이터가 적기 때문이지 않을까 추측
XGBOOST
xgb_models = {}
r2_scores_xgb = {}
for col in Y.columns:
try:
print(f"\nTraining XGBoost for target: {col}")
# XGBoost 모델 생성 (트리 깊이와 트리 개수 줄이기)
xgb = XGBRegressor(n_estimators=50, learning_rate=0.05, max_depth=3, random_state=42)
# 개별 종속 변수(Y)를 대상으로 학습
xgb.fit(X_train, Y_train[col])
# 예측 수행
Y_pred_xgb = xgb.predict(X_test)
# R2 점수 계산
r2_scores_xgb[col] = r2_score(Y_test[col], Y_pred_xgb)
# 모델 저장
xgb_models[col] = xgb
except Exception as e:
print(f"Error while training XGBoost for {col}: {e}")
# 결과 출력
r2_scores_xgb_df = pd.DataFrame.from_dict(r2_scores_xgb, orient='index', columns=['R2 Score'])
print("\n===== XGBoost Regression R2 Scores =====")
print(r2_scores_xgb_df)
랜덤포레스트와 릿지회귀분석 그 사이 쯤의 설명력을 가지는 것을 알 수 있음
R스퀘어 순위를 적어보자면 랜덤포레스트>XGBOOST>릿지회귀분석
환경정보와 생육정보 간의 시각화
import matplotlib.pyplot as plt
import numpy as np
# 모델별 R² 점수 정리
models = ["Ridge Regression", "Random Forest", "XGBoost"]
r2_scores = {
"stemThck": [r2_scores_ridge.get("stemThck", 0), r2_scores_rf.get("stemThck", 0), r2_scores_xgb.get("stemThck", 0)],
"lefCunt": [r2_scores_ridge.get("lefCunt", 0), r2_scores_rf.get("lefCunt", 0), r2_scores_xgb.get("lefCunt", 0)],
"flanGrupp": [r2_scores_ridge.get("flanGrupp", 0), r2_scores_rf.get("flanGrupp", 0), r2_scores_xgb.get("flanGrupp", 0)],
"frtstGrupp": [r2_scores_ridge.get("frtstGrupp", 0), r2_scores_rf.get("frtstGrupp", 0), r2_scores_xgb.get("frtstGrupp", 0)],
}
# 막대 그래프 생성
x = np.arange(len(models))
width = 0.2
fig, ax = plt.subplots(figsize=(10, 6))
# 각 종속변수별로 막대 그래프 생성 및 값 추가
for i, (label, scores) in enumerate(r2_scores.items()):
bars = ax.bar(x + i * width, scores, width, label=label)
# 각 막대 위에 값 표시
for bar in bars:
height = bar.get_height()
ax.text(bar.get_x() + bar.get_width()/2, height, f'{height:.2f}',
ha='center', va='bottom', fontsize=10, color='black')
# 그래프 스타일 설정
ax.set_xlabel("Models")
ax.set_ylabel("R² Score")
ax.set_title("Comparison of Ridge Regression, Random Forest, and XGBoost (R² Score)")
ax.set_xticks(x + width * 1.5)
ax.set_xticklabels(models)
ax.legend(title="Target Variables")
# 그래프 출력
plt.show()
생육정보와 생산정보 간의 관계
릿지회귀분석
# 날짜 포맷 변환
grow_df['measDate'] = pd.to_datetime(grow_df['measDate']).dt.date
prod_df['measDate'] = pd.to_datetime(prod_df['measDate']).dt.date
# 데이터 병합 (생육 데이터 + 생산량 데이터)
merged_data = pd.merge(grow_df, prod_df, on=['measDate', 'farm_cde'], how='inner')
# 병합 후 확인
print("Merged Data Sample:\n", merged_data.head())
# 결측치 개수 확인
print("Missing values in merged_data:\n", merged_data.isnull().sum())
# 독립 변수(X): 생육 지표 (줄기 직경, 엽수, 개화 수준, 착과 수준)
X_columns = ['stemThck', 'lefCunt', 'flanGrupp', 'frtstGrupp']
X = merged_data[X_columns]
# 종속 변수(Y): 수확량 (outtrn)
Y_columns = ['outtrn']
Y = merged_data[Y_columns]
# 결측치 확인
if Y.isnull().all().all():
raise ValueError("All target variables (Y) are NaN after merging. Check measDate and farm_cde.")
# KNN Imputer 적용 (결측치 보완)
imputer = KNNImputer(n_neighbors=5)
if not X.empty:
X = pd.DataFrame(imputer.fit_transform(X), columns=X.columns)
if not Y.empty:
Y = pd.DataFrame(imputer.fit_transform(Y), columns=Y.columns)
# 데이터 스케일링 (X만 적용)
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
# 데이터 분할
X_train, X_test, Y_train, Y_test = train_test_split(X_scaled, Y, test_size=0.2, random_state=42)
# 릿지 회귀 모델 학습
ridge = Ridge(alpha=1.0)
ridge.fit(X_train, Y_train)
# 예측 및 성능 평가
Y_pred = ridge.predict(X_test)
r2_scores_ridge = r2_score(Y_test, Y_pred)
# 결과 출력
print("\n===== Ridge Regression R2 Score =====")
print(f"outtrn (수확량) 예측 R² Score: {r2_scores_ridge:.4f}")
랜덤포레스트
# 랜덤 포레스트 모델 학습
rf = RandomForestRegressor(n_estimators=100, max_depth=10, random_state=42) # max_depth 추가 (과적합 방지)
rf.fit(X_train, Y_train.values.ravel()) # Y 데이터 차원 조정
Y_pred_rf = rf.predict(X_test)
r2_scores_rf = r2_score(Y_test, Y_pred_rf)
# 결과 출력
print("\n===== Model Performance Comparison (R² Score) =====")
print(f"Random Forest R² Score: {r2_scores_rf:.4f}")
XGBOOST
# XGBoost 모델 학습
xgb = XGBRegressor(n_estimators=100, learning_rate=0.1, max_depth=5, random_state=42)
xgb.fit(X_train, Y_train.values.ravel()) # Y 데이터 차원 조정
Y_pred_xgb = xgb.predict(X_test)
r2_scores_xgb = r2_score(Y_test, Y_pred_xgb)
# 결과 출력
print("\n===== Model Performance Comparison (R² Score) =====")
print(f"XGBoost R² Score: {r2_scores_xgb:.4f}")
# 모델별 R² 점수 정리
models = ["Ridge Regression", "Random Forest", "XGBoost"]
r2_scores = [r2_scores_ridge, r2_scores_rf, r2_scores_xgb]
# 막대 그래프 생성
x = np.arange(len(models))
fig, ax = plt.subplots(figsize=(8, 5))
bars = ax.bar(x, r2_scores, color=['blue', 'green', 'orange'])
# 각 막대 위에 R² 값 표시
for bar in bars:
height = bar.get_height()
ax.text(bar.get_x() + bar.get_width()/2, height, f'{height:.4f}',
ha='center', va='bottom', fontsize=10, color='black')
# 그래프 스타일 설정
ax.set_xlabel("Models")
ax.set_ylabel("R² Score")
ax.set_title("Comparison of Ridge, Random Forest, and XGBoost (R² Score)")
ax.set_xticks(x)
ax.set_xticklabels(models)
# 그래프 출력
plt.show()
위에서 했던 환경정보와 생육정보 간의 관계에 비해
생육정보와 생산정보의 관계에는 그다지 설명력이 보이는 점이 확인되지않음
LSTM
딥러닝 알고리즘을 이용한 미래예측
import pandas as pd
import numpy as np
from sklearn.impute import KNNImputer
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split
import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import LSTM, Dense, Dropout
# 데이터 불러오기 (파일 경로에 맞게 수정 필요)
grow_df = pd.read_csv("22_생육정보.csv", encoding='cp949')
prod_df = pd.read_csv("22_생산정보.csv", encoding='cp949')
env_data = pd.read_csv("환경정보_경량.csv", encoding='cp949') # 환경 데이터 불러오기
# 날짜 포맷 변환
grow_df['measDate'] = pd.to_datetime(grow_df['measDate'])
prod_df['measDate'] = pd.to_datetime(prod_df['measDate'])
env_data['measDate'] = pd.to_datetime(env_data['measDate']) # 환경 데이터 날짜 포맷 변환
# 데이터 병합 (생육 데이터 + 생산량 데이터 + 환경 데이터)
merged_data = pd.merge(grow_df, prod_df, on=['measDate', 'farm_cde'], how='inner')
merged_data = pd.merge(merged_data, env_data, on=['measDate', 'farm_cde'], how='inner') # 환경 데이터 병합
# 데이터 정렬 (시간 순서대로)
merged_data = merged_data.sort_values(by=['farm_cde', 'measDate'])
# 결측치 개수 확인
print("Missing values before processing:\n", merged_data.isnull().sum())
# ==========================
# ① 추가 변수 계산 (재배일수 및 개화 후 경과일 추정)
# 재배일수: 첫 측정 날짜를 기준으로 경과일 계산
merged_data['plant_age_days'] = merged_data.groupby('farm_cde')['measDate'].transform(lambda x: (x - x.min()).dt.days)
# 개화 후 경과일(days_after_flowering) 계산 (개화 수준이 특정 값 이상이면 개화 시작일로 설정)
flower_threshold = 5 # 개화 수준 기준값 (조정 가능)
merged_data['first_flanGrupp_date'] = merged_data.groupby('farm_cde')['measDate'].transform(lambda x: x.iloc[0] if merged_data.loc[x.index, 'flanGrupp'].max() < flower_threshold else x[merged_data.loc[x.index, 'flanGrupp'] >= flower_threshold].min())
merged_data['days_after_flowering'] = (merged_data['measDate'] - merged_data['first_flanGrupp_date']).dt.days.fillna(0)
# ② 사용할 변수 선택
X_columns = ['TA', 'HI', 'CI', 'IR', 'stemThck', 'lefCunt', 'flanGrupp', 'frtstGrupp', 'plant_age_days', 'days_after_flowering']
Y_column = 'outtrn' # 종속 변수 (수확량)
# 이상치 조정 함수 정의
def clip_outliers(df, columns_to_check):
for column in columns_to_check:
# IQR 방식으로 이상치 범위 계산 (조정 가능)
Q1 = df[column].quantile(0.25)
Q3 = df[column].quantile(0.75)
IQR = Q3 - Q1
lower_bound = Q1 - 1.5 * IQR
upper_bound = Q3 + 1.5 * IQR
# 이상치를 범위의 경계값으로 대체
df[column] = df[column].clip(lower_bound, upper_bound)
return df
# 이상치 조정에 사용할 컬럼 목록
columns_to_check = ['TA', 'HI', 'CI', 'IR', 'stemThck', 'lefCunt', 'flanGrupp', 'frtstGrupp', 'outtrn']
# 이상치 조정
merged_data = clip_outliers(merged_data, columns_to_check)
# 이후 LSTM 학습 진행
X = merged_data[X_columns]
Y = merged_data[[Y_column]]
# 결측치 처리
imputer = KNNImputer(n_neighbors=5)
X = pd.DataFrame(imputer.fit_transform(X), columns=X.columns)
Y = pd.DataFrame(imputer.fit_transform(Y), columns=Y.columns)
# 데이터 정규화
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
# ==========================
# ⑤ 시계열 데이터 생성 (LSTM 입력 데이터 변환)
def create_time_series_data(X, Y, time_steps=30):
X_series, Y_series = [], []
for i in range(len(X) - time_steps):
X_series.append(X[i:i+time_steps]) # 최근 time_steps일치 데이터 입력
Y_series.append(Y[i+time_steps]) # time_steps 이후의 수확량 예측값
return np.array(X_series), np.array(Y_series)
# LSTM 입력 데이터 변환
time_steps = 30
X_lstm, Y_lstm = create_time_series_data(X_scaled, Y.values, time_steps)
# 데이터 분할 및 LSTM 실행
X_train, X_test, Y_train, Y_test = train_test_split(X_lstm, Y_lstm, test_size=0.2, random_state=42)
model.fit(X_train, Y_train, epochs=50, batch_size=16, validation_data=(X_test, Y_test))
print("LSTM Input Shape:", X_train.shape) # (batch_size, time_steps, features)
# LSTM 모델 구축
model = Sequential([
LSTM(64, activation='relu', return_sequences=True, input_shape=(time_steps, X_train.shape[2])),
Dropout(0.2),
LSTM(32, activation='relu', return_sequences=False),
Dropout(0.2),
Dense(16, activation='relu'),
Dense(1) # 수확량 예측
])
# 모델 컴파일
model.compile(optimizer='adam', loss='mse', metrics=['mae'])
# 모델 학습
history = model.fit(X_train, Y_train, epochs=50, batch_size=16, validation_data=(X_test, Y_test))
# 예측 수행
Y_pred_lstm = model.predict(X_test)
from sklearn.metrics import r2_score
# R² 점수 계산
r2_score_lstm = r2_score(Y_test, Y_pred_lstm)
# 결과 출력
print("\n===== LSTM Model Performance =====")
print(f"LSTM R² Score: {r2_score_lstm:.4f}")
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
# 테스트 데이터에 해당하는 날짜 가져오기
test_dates = merged_data['measDate'].iloc[-len(Y_test):].values
# DataFrame 생성 (날짜, 실제값, 예측값 매칭)
df_results = pd.DataFrame({'Date': test_dates, 'Actual': Y_test.flatten(), 'Prediction': Y_pred_lstm.flatten()})
# 날짜별 평균값을 계산하여 중복 제거
df_results = df_results.groupby('Date').mean().reset_index()
plt.figure(figsize=(12, 6))
# 날짜별 선형 그래프 (중복 제거 후 평균값 사용)
plt.plot(df_results['Date'], df_results['Actual'], label="Actual", marker='o', linestyle='-', color='blue')
plt.plot(df_results['Date'], df_results['Prediction'], label="LSTM Prediction", marker='o', linestyle='-', color='orange')
# x축 날짜 포맷 조정
plt.xticks(rotation=45)
plt.xlabel("Date")
plt.ylabel("Yield (outtrn)")
plt.title("LSTM Prediction vs Actual Yield (Time Series)")
plt.legend()
plt.grid()
plt.show()
추가 : 환경제어정보 시각화
import seaborn as sns
import matplotlib.pyplot as plt
# 생육 지표 및 환경 변수 간의 상관계수 계산
correlation_matrix = merged_data[['TA', 'HI', 'CI', 'IR', 'stemThck', 'lefCunt', 'flanGrupp', 'frtstGrupp']].corr()
# 히트맵으로 시각화
plt.figure(figsize=(10, 6))
sns.heatmap(correlation_matrix, annot=True, cmap='coolwarm', fmt=".2f", linewidths=0.5)
plt.title("Correlation Matrix: Environmental Factors & Growth Indicators")
plt.show()
# 독립 변수(X): 환경 변수
X = merged_data[['TA', 'HI', 'CI', 'IR']]
# 종속 변수(Y): 생육 지표 중 하나 (예: stemThck)
Y = merged_data['stemThck']
# 데이터 분할
X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=0.2, random_state=42)
# 랜덤 포레스트 모델 학습
rf = RandomForestRegressor(n_estimators=100, random_state=42)
rf.fit(X_train, Y_train)
# Feature Importance 분석
feature_importance = pd.DataFrame({'Feature': X.columns, 'Importance': rf.feature_importances_})
feature_importance = feature_importance.sort_values(by='Importance', ascending=False)
# 결과 출력
print("Feature Importance (Environmental Factors -> Growth):")
print(feature_importance)
# Feature Importance 데이터
feature_importance = pd.DataFrame({
'Feature': ['IR', 'HI', 'CI', 'TA'],
'Importance': [0.477522, 0.356100, 0.094141, 0.072237]
})
# 정렬 (중요도가 높은 순서대로)
feature_importance = feature_importance.sort_values(by='Importance', ascending=True)
# 막대 그래프 그리기
plt.figure(figsize=(8, 5))
sns.barplot(y=feature_importance['Feature'], x=feature_importance['Importance'], palette='coolwarm')
# 그래프 스타일 설정
plt.xlabel("Feature Importance")
plt.ylabel("Environmental Factors")
plt.title("Feature Importance of Environmental Factors on Growth Indicators")
plt.grid(axis='x', linestyle="--", alpha=0.7)
# 값 표시
for index, value in enumerate(feature_importance['Importance']):
plt.text(value + 0.01, index, f"{value:.3f}", va='center', fontsize=10)
plt.show()