{
 "nbformat": 4,
 "nbformat_minor": 0,
 "metadata": {
  "colab": {
   "provenance": []
  },
  "kernelspec": {
   "name": "python3",
   "display_name": "Python 3"
  },
  "language_info": {
   "name": "python"
  }
 },
 "cells": [
  {
   "cell_type": "markdown",
   "source": [
    "# 1. 데이터 임포트 및 데이터 확인\n"
   ],
   "metadata": {
    "id": "yDf9iEYmr1fN"
   }
  },
  {
   "cell_type": "code",
   "source": [
    "# 데이터 분석에 필요한 모듈 import\n",
    "import pandas as pd\n",
    "import numpy as np"
   ],
   "metadata": {
    "id": "5HKawsgPr8Od"
   },
   "execution_count": 1,
   "outputs": []
  },
  {
   "cell_type": "code",
   "source": [
    "# 데이터 임포트\n",
    "df = pd.read_csv('housing_linear_regression_dataset.csv')\n",
    "print(df)"
   ],
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 444
    },
    "collapsed": true,
    "id": "8SpUH2uMsPmB",
    "outputId": "1e2b21b0-2545-4c53-de85-a4da3135129d"
   },
   "execution_count": 2,
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "     record_id  area_m2  bedrooms  building_age_years  distance_to_subway_m  floor      district  has_parking  \\\n",
      "0            1     81.3         3                   3                1822.0      8         Nowon            1   \n",
      "1            2     60.6         2                  16                 918.0     25         Nowon            1   \n",
      "2            3     43.9         1                  24                 980.0     25          Mapo            1   \n",
      "3            4     41.3         1                  15                1432.0     15        Gwanak            1   \n",
      "4            5    123.2         4                  16                 934.0     13         Nowon            1   \n",
      "..         ...      ...       ...                 ...                   ...    ...           ...          ...   \n",
      "295        296    106.5         5                   1                 569.0     25        Songpa            1   \n",
      "296        297     71.1         3                  20                1630.0     34  Yeongdeungpo            0   \n",
      "297        298     98.7         3                   3                 966.0     24        Songpa            1   \n",
      "298        299     42.8         2                  24                   NaN      5        Gwanak            1   \n",
      "299        300     60.8         2                  29                1740.0     29  Yeongdeungpo            1   \n",
      "\n",
      "     monthly_maintenance_fee listing_source    agent_note  sale_price_10k_krw  \n",
      "0                    23000.0         broker     renovated               13033  \n",
      "1                    43000.0         broker   corner_unit                9838  \n",
      "2                    61000.0         broker     renovated                6776  \n",
      "3                    48000.0       referral   near_school                6629  \n",
      "4                    69000.0         online     renovated               16692  \n",
      "..                       ...            ...           ...                 ...  \n",
      "295                      NaN       referral     renovated               17209  \n",
      "296                  54000.0         broker   corner_unit               10882  \n",
      "297                  38000.0         broker  quiet_street               14919  \n",
      "298                  60000.0         broker   near_school                7603  \n",
      "299                  72000.0       referral   near_school                8923  \n",
      "\n",
      "[300 rows x 12 columns]\n"
     ]
    }
   ]
  },
  {
   "cell_type": "code",
   "source": [
    "# 1. 데이터의 정보: info()는 직접 출력하므로 print()로 감싸지 않습니다.\n",
    "df.info()\n",
    "\n",
    "# 2. 데이터의 첫 5행, 마지막 5행\n",
    "print(df.head())\n",
    "print(df.tail())\n",
    "\n",
    "# 3. 데이터에 대한 산술통계량\n",
    "print(df.describe())"
   ],
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 650
    },
    "collapsed": true,
    "id": "2RImlRSMsqQq",
    "outputId": "2bb5ab28-8638-41b5-ba68-11fbca7ded55"
   },
   "execution_count": 3,
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "<class 'pandas.DataFrame'>\n",
      "RangeIndex: 300 entries, 0 to 299\n",
      "Data columns (total 12 columns):\n",
      " #   Column                   Non-Null Count  Dtype  \n",
      "---  ------                   --------------  -----  \n",
      " 0   record_id                300 non-null    int64  \n",
      " 1   area_m2                  285 non-null    float64\n",
      " 2   bedrooms                 300 non-null    int64  \n",
      " 3   building_age_years       300 non-null    int64  \n",
      " 4   distance_to_subway_m     287 non-null    float64\n",
      " 5   floor                    300 non-null    int64  \n",
      " 6   district                 291 non-null    str    \n",
      " 7   has_parking              300 non-null    int64  \n",
      " 8   monthly_maintenance_fee  292 non-null    float64\n",
      " 9   listing_source           300 non-null    str    \n",
      " 10  agent_note               300 non-null    str    \n",
      " 11  sale_price_10k_krw       300 non-null    int64  \n",
      "dtypes: float64(3), int64(6), str(3)\n",
      "memory usage: 28.3 KB\n",
      "   record_id  area_m2  bedrooms  building_age_years  distance_to_subway_m  floor district  has_parking  \\\n",
      "0          1     81.3         3                   3                1822.0      8    Nowon            1   \n",
      "1          2     60.6         2                  16                 918.0     25    Nowon            1   \n",
      "2          3     43.9         1                  24                 980.0     25     Mapo            1   \n",
      "3          4     41.3         1                  15                1432.0     15   Gwanak            1   \n",
      "4          5    123.2         4                  16                 934.0     13    Nowon            1   \n",
      "\n",
      "   monthly_maintenance_fee listing_source   agent_note  sale_price_10k_krw  \n",
      "0                  23000.0         broker    renovated               13033  \n",
      "1                  43000.0         broker  corner_unit                9838  \n",
      "2                  61000.0         broker    renovated                6776  \n",
      "3                  48000.0       referral  near_school                6629  \n",
      "4                  69000.0         online    renovated               16692  \n",
      "     record_id  area_m2  bedrooms  building_age_years  distance_to_subway_m  floor      district  has_parking  \\\n",
      "295        296    106.5         5                   1                 569.0     25        Songpa            1   \n",
      "296        297     71.1         3                  20                1630.0     34  Yeongdeungpo            0   \n",
      "297        298     98.7         3                   3                 966.0     24        Songpa            1   \n",
      "298        299     42.8         2                  24                   NaN      5        Gwanak            1   \n",
      "299        300     60.8         2                  29                1740.0     29  Yeongdeungpo            1   \n",
      "\n",
      "     monthly_maintenance_fee listing_source    agent_note  sale_price_10k_krw  \n",
      "295                      NaN       referral     renovated               17209  \n",
      "296                  54000.0         broker   corner_unit               10882  \n",
      "297                  38000.0         broker  quiet_street               14919  \n",
      "298                  60000.0         broker   near_school                7603  \n",
      "299                  72000.0       referral   near_school                8923  \n",
      "        record_id     area_m2    bedrooms  building_age_years  distance_to_subway_m       floor  has_parking  \\\n",
      "count  300.000000  285.000000  300.000000          300.000000            287.000000  300.000000   300.000000   \n",
      "mean   150.500000   91.475789    3.036667           15.733333           1050.923345   18.420000     0.720000   \n",
      "std     86.746758   32.520624    1.188851            8.533363            538.255348    9.912169     0.449749   \n",
      "min      1.000000   35.000000    1.000000            1.000000             98.000000    1.000000     0.000000   \n",
      "25%     75.750000   64.300000    2.000000            8.750000            634.000000   10.000000     0.000000   \n",
      "50%    150.500000   87.900000    3.000000           15.500000           1061.000000   18.500000     1.000000   \n",
      "75%    225.250000  122.100000    4.000000           23.000000           1510.000000   27.000000     1.000000   \n",
      "max    300.000000  149.600000    5.000000           30.000000           1965.000000   35.000000     1.000000   \n",
      "\n",
      "       monthly_maintenance_fee  sale_price_10k_krw  \n",
      "count               292.000000          300.000000  \n",
      "mean              57547.945205        13018.893333  \n",
      "std               19426.690886         4093.948471  \n",
      "min               12000.000000         5003.000000  \n",
      "25%               43000.000000         9399.000000  \n",
      "50%               59000.000000        12584.500000  \n",
      "75%               72000.000000        16662.250000  \n",
      "max               99000.000000        21079.000000  \n"
     ]
    }
   ]
  },
  {
   "cell_type": "markdown",
   "source": [
    "# 2. 필요없는 열 제거 및 데이터 분리"
   ],
   "metadata": {
    "id": "MrO7jbtV-yUv"
   }
  },
  {
   "cell_type": "code",
   "source": [
    "# 필요없는 열 삭제하기\n",
    "df = df.drop(columns=['record_id', 'district', 'listing_source', 'agent_note'])\n",
    "print(df)"
   ],
   "metadata": {
    "id": "YFfkVStE-9iY",
    "collapsed": true
   },
   "execution_count": 4,
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "     area_m2  bedrooms  building_age_years  distance_to_subway_m  floor  has_parking  monthly_maintenance_fee  \\\n",
      "0       81.3         3                   3                1822.0      8            1                  23000.0   \n",
      "1       60.6         2                  16                 918.0     25            1                  43000.0   \n",
      "2       43.9         1                  24                 980.0     25            1                  61000.0   \n",
      "3       41.3         1                  15                1432.0     15            1                  48000.0   \n",
      "4      123.2         4                  16                 934.0     13            1                  69000.0   \n",
      "..       ...       ...                 ...                   ...    ...          ...                      ...   \n",
      "295    106.5         5                   1                 569.0     25            1                      NaN   \n",
      "296     71.1         3                  20                1630.0     34            0                  54000.0   \n",
      "297     98.7         3                   3                 966.0     24            1                  38000.0   \n",
      "298     42.8         2                  24                   NaN      5            1                  60000.0   \n",
      "299     60.8         2                  29                1740.0     29            1                  72000.0   \n",
      "\n",
      "     sale_price_10k_krw  \n",
      "0                 13033  \n",
      "1                  9838  \n",
      "2                  6776  \n",
      "3                  6629  \n",
      "4                 16692  \n",
      "..                  ...  \n",
      "295               17209  \n",
      "296               10882  \n",
      "297               14919  \n",
      "298                7603  \n",
      "299                8923  \n",
      "\n",
      "[300 rows x 8 columns]\n"
     ]
    }
   ]
  },
  {
   "cell_type": "code",
   "source": [
    "# 독립변수, 종속변수 분리하기\n",
    "X = df.drop(columns=[\"sale_price_10k_krw\"])\n",
    "Y = df[\"sale_price_10k_krw\"]\n",
    "print(X.head())\n",
    "print(Y.head())"
   ],
   "metadata": {
    "id": "dWnW9d2o_ND7"
   },
   "execution_count": 5,
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "   area_m2  bedrooms  building_age_years  distance_to_subway_m  floor  has_parking  monthly_maintenance_fee\n",
      "0     81.3         3                   3                1822.0      8            1                  23000.0\n",
      "1     60.6         2                  16                 918.0     25            1                  43000.0\n",
      "2     43.9         1                  24                 980.0     25            1                  61000.0\n",
      "3     41.3         1                  15                1432.0     15            1                  48000.0\n",
      "4    123.2         4                  16                 934.0     13            1                  69000.0\n",
      "0    13033\n",
      "1     9838\n",
      "2     6776\n",
      "3     6629\n",
      "4    16692\n",
      "Name: sale_price_10k_krw, dtype: int64\n"
     ]
    }
   ]
  },
  {
   "cell_type": "code",
   "source": [
    "# 훈련데이터, 테스트데이터 분리를 도와주는 함수 불러오기\n",
    "from sklearn.model_selection import train_test_split"
   ],
   "metadata": {
    "id": "mMCjut70HKgQ"
   },
   "execution_count": 6,
   "outputs": []
  },
  {
   "cell_type": "code",
   "source": [
    "# 훈련데이터, 테스트데이터 분리하기\n",
    "X_train, X_test, Y_train, Y_test = train_test_split(\n",
    "    X, Y, test_size=0.2, random_state=42\n",
    ")\n",
    "print(\"훈련 데이터 크기:\", X_train.shape, Y_train.shape)\n",
    "print(\"테스트 데이터 크기:\", X_test.shape, Y_test.shape)"
   ],
   "metadata": {
    "id": "kNr6n3qsBAUe"
   },
   "execution_count": 7,
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "훈련 데이터 크기: (240, 7) (240,)\n",
      "테스트 데이터 크기: (60, 7) (60,)\n"
     ]
    }
   ]
  },
  {
   "cell_type": "markdown",
   "source": [
    "# 3. 결측치 처리 및 데이터 표준화"
   ],
   "metadata": {
    "id": "l47WWSWNBzFx"
   }
  },
  {
   "cell_type": "code",
   "source": [
    "# 결측치 여부 확인\n",
    "print(X_train.isnull().sum())"
   ],
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 304
    },
    "collapsed": true,
    "id": "BvwRfDklBMad",
    "outputId": "38b02c85-901b-44b7-d19c-7ca9beb60aa5"
   },
   "execution_count": 8,
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "area_m2                    13\n",
      "bedrooms                    0\n",
      "building_age_years          0\n",
      "distance_to_subway_m       11\n",
      "floor                       0\n",
      "has_parking                 0\n",
      "monthly_maintenance_fee     5\n",
      "dtype: int64\n"
     ]
    }
   ]
  },
  {
   "cell_type": "code",
   "source": [
    "# 결측치를 자동으로 처리해주는 객체 생성기 import\n",
    "from sklearn.impute import SimpleImputer"
   ],
   "metadata": {
    "id": "IpwXSDf6CCV3"
   },
   "execution_count": 9,
   "outputs": []
  },
  {
   "cell_type": "code",
   "source": [
    "# 결측치 처리 인스턴스 생성\n",
    "imputer = SimpleImputer(strategy=\"mean\")"
   ],
   "metadata": {
    "id": "adxyt8ELDdWl"
   },
   "execution_count": 10,
   "outputs": []
  },
  {
   "cell_type": "code",
   "source": [
    "# 결측치 처리. 이때 train 데이터의 결측치를 처리하는 데 쓰인 값(train 데이터의 평균)을 그대로 test 데이터에 써야함.\n",
    "# 왜? 테스트 데이터의 평균으로 테스트 데이터의 결측치를 처리하면 이건 미리 test 데이터의 정보를 알게되는 꼴이기 때문이다.\n",
    "# 테스트 데이터는 반드시 예측 전까지 '모르는 데이터'여야 하기 때문에 결측치를 훈련 데이터의 평균으로 채워야함.. 매우 중요.\n",
    "X_train_imputed = imputer.fit_transform(X_train)\n",
    "X_test_imputed = imputer.transform(X_test)\n",
    "print(\"결측치 처리에 사용한 훈련 평균:\", imputer.statistics_)\n",
    "print(pd.DataFrame(X_train_imputed, columns=X.columns).head())\n",
    "print(\"처리 후 남은 결측치 수:\", np.isnan(X_train_imputed).sum())"
   ],
   "metadata": {
    "id": "QxNIwCsmDlCv"
   },
   "execution_count": 11,
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "결측치 처리에 사용한 훈련 평균: [9.194493e+01 3.033333e+00 1.571250e+01 1.042581e+03 1.835000e+01 7.208333e-01 5.751064e+04]\n",
      "   area_m2  bedrooms  building_age_years  distance_to_subway_m  floor  has_parking  monthly_maintenance_fee\n",
      "0    113.0       3.0                13.0                1219.0   16.0          1.0                  63000.0\n",
      "1     98.1       4.0                29.0                 128.0   32.0          0.0                  87000.0\n",
      "2     68.1       3.0                20.0                1372.0   28.0          1.0                  66000.0\n",
      "3    115.3       4.0                17.0                1259.0   29.0          0.0                  71000.0\n",
      "4     78.0       2.0                29.0                1792.0   30.0          0.0                  79000.0\n",
      "처리 후 남은 결측치 수: 0\n"
     ]
    }
   ]
  },
  {
   "cell_type": "code",
   "source": [
    "# 데이터 표준화를 처리해주는 객체 생성기 import\n",
    "from sklearn.preprocessing import StandardScaler"
   ],
   "metadata": {
    "id": "Gpl-F1O8E1v2"
   },
   "execution_count": 12,
   "outputs": []
  },
  {
   "cell_type": "code",
   "source": [
    "# 데이터 표준화 인스턴스 생성\n",
    "scaler = StandardScaler()"
   ],
   "metadata": {
    "id": "6QXU3piGGEqj"
   },
   "execution_count": 13,
   "outputs": []
  },
  {
   "cell_type": "code",
   "source": [
    "# 표준화. 이때 아까 결측치에서와 마찬가지로 train 데이터를 표준화하는데 쓰인 평균, 표준편차를 그대로 test 데이터에 적용해야한다.\n",
    "# 왜? 아까와 마찬가지로, test 데이터의 평균과 표준편차를 갖고 test 데이터를 표준화하면 그것은 미리 test 데이터의 정보를 알고있는 것이나 다름아니게 되기 때문에..\n",
    "X_train_scaled = scaler.fit_transform(X_train_imputed)\n",
    "X_test_scaled = scaler.transform(X_test_imputed)\n",
    "print(\"표준화에 사용한 훈련 평균:\", scaler.mean_)\n",
    "print(\"표준화에 사용한 훈련 표준편차:\", scaler.scale_)\n",
    "print(\"표준화한 훈련 데이터의 열별 평균:\", X_train_scaled.mean(axis=0))"
   ],
   "metadata": {
    "id": "dGL07qmCGKpx"
   },
   "execution_count": 14,
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "표준화에 사용한 훈련 평균: [9.194493e+01 3.033333e+00 1.571250e+01 1.042581e+03 1.835000e+01 7.208333e-01 5.751064e+04]\n",
      "표준화에 사용한 훈련 표준편차: [3.144450e+01 1.175679e+00 8.635480e+00 5.339145e+02 9.899200e+00 4.485896e-01 1.939273e+04]\n",
      "표준화한 훈련 데이터의 열별 평균: [ 1.498801e-16  9.621933e-17 -3.700743e-17  2.812565e-16 -1.258253e-16  5.736152e-17 -4.144833e-16]\n"
     ]
    }
   ]
  },
  {
   "cell_type": "code",
   "source": [
    "# 표준화한 훈련 데이터 확인\n",
    "print(X_train_scaled)"
   ],
   "metadata": {
    "id": "ti71tiekJQcu",
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "outputId": "f90a37d2-a291-4fa2-f5db-6fcc527a7681"
   },
   "execution_count": 15,
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "[[ 0.669594 -0.028352 -0.314111 ... -0.237393  0.622321  0.283063]\n",
      " [ 0.195744  0.82222   1.53871  ...  1.378899 -1.606888  1.52064 ]\n",
      " [-0.758318 -0.028352  0.496498 ...  0.974826  0.622321  0.43776 ]\n",
      " ...\n",
      " [ 1.003516  0.82222  -0.893118 ...  0.469735  0.622321 -0.232594]\n",
      " [-0.961851 -0.878924  0.264896 ... -1.651649  0.622321  0.231497]\n",
      " [-0.424396 -0.028352  0.843902 ... -0.338411  0.622321  0.901851]]\n"
     ]
    }
   ]
  },
  {
   "cell_type": "markdown",
   "source": [
    "# 4. 모델 생성 및 학습, 예측\n"
   ],
   "metadata": {
    "id": "2eixL3ld4MF3"
   }
  },
  {
   "cell_type": "code",
   "source": [
    "# 선형회귀 모델 불러오기\n",
    "from sklearn.linear_model import SGDRegressor"
   ],
   "metadata": {
    "id": "4nbV8O3DX3dl"
   },
   "execution_count": 16,
   "outputs": []
  },
  {
   "cell_type": "code",
   "source": [
    "# 모델 생성하기\n",
    "model = SGDRegressor(\n",
    "    eta0=0.01,\n",
    "    max_iter=1000000,\n",
    "    tol=1e-3,\n",
    "    random_state=42\n",
    ")"
   ],
   "metadata": {
    "id": "jpK_MA-y4XuZ"
   },
   "execution_count": 17,
   "outputs": []
  },
  {
   "cell_type": "code",
   "source": [
    "# 표준화한 훈련 데이터로 학습\n",
    "model.fit(X_train_scaled, Y_train)\n",
    "print(\"학습한 가중치:\", model.coef_)\n",
    "print(\"학습한 절편:\", model.intercept_)\n",
    "print(\"학습 반복 횟수:\", model.n_iter_)"
   ],
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/",
     "height": 80
    },
    "id": "ennRoqc94faI",
    "outputId": "82ff79b8-3827-4989-8f73-566eac9b6bdb"
   },
   "execution_count": 18,
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "학습한 가중치: [ 2195.064149  1324.442795 -1073.126917  -177.101338   382.613649   322.183378   885.61344 ]\n",
      "학습한 절편: [13041.345865]\n",
      "학습 반복 횟수: 95\n"
     ]
    }
   ]
  },
  {
   "cell_type": "code",
   "source": [
    "# 같은 기준으로 표준화한 테스트 데이터로 예측\n",
    "Y_pred = model.predict(X_test_scaled)\n",
    "print(pd.DataFrame({\n",
    "    \"실제 가격\": Y_test.to_numpy(),\n",
    "    \"예측 가격\": Y_pred\n",
    "}).head(10))"
   ],
   "metadata": {
    "id": "Cj020rmA4kcW"
   },
   "execution_count": 19,
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "   실제 가격         예측 가격\n",
      "0  13295  14118.252285\n",
      "1   7339   7562.460494\n",
      "2  12684  13871.255820\n",
      "3  11096  10113.482203\n",
      "4  18067  17696.526913\n",
      "5   9348   9117.167470\n",
      "6   8897   9514.700588\n",
      "7   8200   9040.201434\n",
      "8  10871  10546.440613\n",
      "9  16672  17058.001075\n"
     ]
    }
   ]
  },
  {
   "cell_type": "markdown",
   "source": [
    "# 5. 성능지표 출력"
   ],
   "metadata": {
    "id": "GqfaFp5w8qUc"
   }
  },
  {
   "cell_type": "code",
   "source": [
    "#성능지표를 계산해주는 함수 불러오기\n",
    "from sklearn.metrics import r2_score"
   ],
   "metadata": {
    "id": "kZP-amPL5d4L"
   },
   "execution_count": 20,
   "outputs": []
  },
  {
   "cell_type": "code",
   "source": [
    "# R² 점수 확인\n",
    "print(r2_score(Y_test, Y_pred))"
   ],
   "metadata": {
    "colab": {
     "base_uri": "https://localhost:8080/"
    },
    "id": "PHIjdxiz8ztt",
    "outputId": "c33d76e6-c6fb-4830-9957-e31e12c7f33c"
   },
   "execution_count": 21,
   "outputs": [
    {
     "output_type": "stream",
     "name": "stdout",
     "text": [
      "0.9137598723918361\n"
     ]
    }
   ]
  }
 ]
}
