2. 비교 분석¶

주차에 따라 작물의 평균 생장 상태 대비 현재 내 작물은 얼마나 잘 자라고 있는지 비교


2.1 데이터 불러오기¶

2.1.1 모듈 불러오기¶

In [1]:
import numpy as np
import pandas as pd
import datetime as dt

import seaborn as sns
import statsmodels.api as sm
import matplotlib.pyplot as plt
from matplotlib import font_manager, rc

from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import MinMaxScaler

# 폰트 설정
font_path = "C:/Windows/Fonts/malgun.ttf"  # 한글 폰트 경로
font_name = font_manager.FontProperties(fname=font_path).get_name()
rc('font', family=font_name)

2.1.2 파일 불러오기¶

  • File 경로를 입력합니다.

    root = 'C:/AI_Project/AI/12_smartfarm/data'

  • 불러오고자 하는 csv file의 이름을 입력합니다.
    충남 부여 연동 방울토마토 재배 비닐 하우스 농가 3곳 데이터(cny1, cny2, cny3)

    cny1 = pd.read_csv(root+'/cny1.csv', encoding='cp949')
    cny2 = pd.read_csv(root+'/cny2.csv', encoding='cp949')
    cny3 = pd.read_csv(root+'/cny3.csv', encoding='cp949')

In [2]:
# .csv 파일이 존재하는 폴더
root = 'C:/AI_Project/AI/12_smartfarm/data'

cny1 = pd.read_csv(root+'/cny1.csv', encoding='cp949')
cny2 = pd.read_csv(root+'/cny2.csv', encoding='cp949')
cny3 = pd.read_csv(root+'/cny3.csv', encoding='cp949')

2.1.3 분석하고자 하는 데이터 선택¶

  • 예시에서는 20주차때 사이즈가 엽장이 31cm인 작물에 대해 분석합니다.
    • 주차별 엽장 길이 분포를 통해 대상 작물이 일반 작물 대비 어느정도에 위치하는지 분석
    • 이전에 도출한 생육 모델을 바탕으로 일반적으로 해당 주차에는 엽장의 길이가 어느정도인지 도출
    • 도출한 결과를 바탕으로 신뢰구간 도출
    • 신뢰구간을 벗어난 길이인 경우 일반적인 샘플들과 비해 확연하게 차이를 보인다고 할 수 있음
In [3]:
## 예시 사이즈 : 엽장이 20주차 31일 때
input_week = int(input("작물의 주차 :  "))

input_size = float(input("분석하고자 하는 작물의 사이즈 :  "))
작물의 주차 :  20
분석하고자 하는 작물의 사이즈 :  31

2.2 모델 학습¶

2.2.1 데이터 전처리 및 모델 학습¶

In [4]:
# 데이터 결측치 처리 및 주차 컬럼 형식 변경
def data_preprocess(dataframe, columns=[]):
    if columns:
        dataframe = dataframe[columns]
        
    df_colDel = dataframe.dropna(axis=1, how='all')
    df_rowDel = df_colDel.dropna(subset=['주차'])
    df_rowDel['주차'] = df_rowDel['주차'].str.extract('(\d+)').astype(int)
    
    df_unique_rows = df_rowDel.drop_duplicates()
    df = df_unique_rows.reset_index(drop=True)
    
    return df

# 신뢰 구간을 포함한 Linear Regression Model
def linear_regression_with_confidence_interval(df, xcol, ycol):
    df_cleaned = df.dropna(subset=[xcol, ycol]) # NaN이 있는 행 제외

    # 선형 회귀 모델 초기화 및 훈련
    X = sm.add_constant(df_cleaned[[xcol]])
    model = sm.OLS(df_cleaned[ycol], X).fit()

    # 95% 신뢰 구간 계산
    ci = model.get_prediction(X).conf_int(alpha=0.05)

    # 예측값
    predicted_values = model.predict(X)
    
    # 결과를 데이터프레임으로 생성
    result_df = pd.DataFrame({
        xcol: df_cleaned[xcol],
        'lowerbound': ci[:, 0],
        'upperbound': ci[:, 1],
    })

    return predicted_values, result_df

# Visualization Linear Regression Model
def print_LR(df_list, xcol, ycol, input_week, input_size):
    df_concat = pd.concat(df_list, axis=0)
    df_concat = df_concat.reset_index(drop=True)
    
    df = df_concat.dropna(subset=[xcol, ycol])
    X = df[[xcol]]
    y = df[ycol]
    
    # 선형 회귀 모델 초기화 및 훈련
    model = LinearRegression()
    model.fit(X, y)
    
    # 함수 호출
    predicted_values, confidence_interval = linear_regression_with_confidence_interval(df_concat, xcol=xcol, ycol=ycol) # 예측값, 신뢰구간
    
    # 회귀선 시각화
    plt.figure(figsize=(8, 6))
    sns.regplot(x=xcol, y=ycol, data=df)
    plt.plot(df[xcol], model.predict(X), color='red', linewidth=2, label='Linear Regression')
    plt.scatter(input_week, input_size, color='red', s=20, label='Input Value')
    
    plt.title(f'Linear Regression: {ycol} - {xcol}')
    plt.xlabel(xcol)
    plt.ylabel(ycol)
    plt.legend()
    plt.grid(True, linestyle='--', alpha=0.7)
    plt.show()

    # 회귀 계수 및 절편 출력
    print(f'Intercept (절편): {model.intercept_}')
    print(f'Coefficient (기울기): {model.coef_[0]}')
    
    return model.intercept_, model.coef_[0], predicted_values, confidence_interval

2.2.2 분석 요소 선택¶

  • 독립 변수와 종속 변수를 입력합니다. (본 예제에서는 생육 분석때 사용한 모델 사용. 주차를 독립 변수로, 엽장을 종속 변수로 활용)
  • xcol = '주차'
  • ycol = '엽장'
In [5]:
processed_cny1 = data_preprocess(cny1, ['시설ID', '조사일', '주차', '생장길이', '화방높이', '줄기직경', '엽장', '엽폭', '엽수', '개화군', '착과군', '열매수', '최종화방차수'])
processed_cny2 = data_preprocess(cny2, ['시설ID', '조사일', '주차', '생장길이', '화방높이', '줄기직경', '엽장', '엽폭', '엽수', '개화군', '착과군', '열매수', '최종화방차수'])
processed_cny3 = data_preprocess(cny3, ['시설ID', '조사일', '주차', '생장길이', '화방높이', '줄기직경', '엽장', '엽폭', '엽수', '개화군', '착과군', '열매수', '최종화방차수'])

# x-axis = 주차 / y-axis = 엽장
xcol = '주차'
ycol = '엽장'

# print_LR 함수를 통해 그래프 도출
intercept, coef, predicted_values, confidence_interval = print_LR([processed_cny1, processed_cny2, processed_cny3], xcol, ycol, input_week, input_size)
C:\Users\정하영\AppData\Local\Temp/ipykernel_10560/1572217170.py:7: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame.
Try using .loc[row_indexer,col_indexer] = value instead

See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  df_rowDel['주차'] = df_rowDel['주차'].str.extract('(\d+)').astype(int)
C:\Anaconda3\lib\site-packages\statsmodels\tsa\tsatools.py:142: FutureWarning: In a future version of pandas all arguments of concat except for the argument 'objs' will be keyword-only
  x = pd.concat(x[::order], 1)
Intercept (절편): 17.75582034520376
Coefficient (기울기): 1.0331842129743172
  • 비교 분석 대상 작물(빨간 점)이 주차별 엽장의 일반적인 분포 및 신뢰구간에서 벗어나 있음
  • 확실하게 분석 대상 작물이 발육이 부진한 것을 알 수 있음

2.2.3 모델 활용¶

In [6]:
y_pred = coef*input_week+intercept

print('===========================================================================\n')
print(f'{input_week}{xcol} 일 때, 예상 {ycol} 사이즈는 {round(y_pred, 2)} 입니다.\n')
print(f'해당 작물은 예상 크기의 {round(input_size/y_pred*100,2)}% 사이즈 입니다.')
print('\n===========================================================================')
===========================================================================

20주차 일 때, 예상 엽장 사이즈는 38.42 입니다.

해당 작물은 예상 크기의 80.69% 사이즈 입니다.

===========================================================================