Pythonではじめるデータ分析:宿泊価格予測入門-実践編-

PR表記

※アフィリエイト広告を利用しています

最後に実際のデータを使った実践編をご紹介します。ぜひ自分で行ってみた結果と照らし合わせてみてください。

全編はこちらから

目次

物件データの収集

STEP
必要なライブラリのインポート

まず、データ分析に必要なライブラリをインポートします。

import pandas as pd # データフレーム操作
import numpy as np  # 数値計算
STEP
CSVファイルの読み込み

次に、CSVファイルを読み込み、pandasのデータフレームとして格納します。

df = pd.read_csv("宿泊価格について_demo.csv")

# 読み込んだデータフレームの最初の5行を表示して確認
print(df.head())
STEP
欠損値の確認

データフレームの各列に欠損値(NaN)がどれだけ含まれているかを確認します。

# 各列の欠損値の数を表示
print(df.isnull().sum())
STEP
 欠損値のある行の確認

欠損値がどの行にあるかを確認します。

# 欠損値を含む行を抽出
missing_values_rows = df[df.isnull().any(axis=1)]
print(missing_values_rows)
STEP
欠損値の修正

欠損値が見つかった列について、適切な方法で修正します。今回のデータでは bedrooms と beds 列に欠損値があります。

ここでは以下の戦略をとります。

  • bedrooms 列の欠損値は、同じ room_type で、かつ accommodates (収容人数) が同じ値の行の bedrooms の最頻値(一番多い値)で補完します。
  • beds 列の欠損値は、同じ room_type で、かつ accommodates (収容人数) が同じ値の行の beds の最頻値(一番多い値)で補完します。
def fill_missing_with_mode(df, column_to_fill, group_columns):
    """
    指定されたグループ列に基づいて、欠損値を最頻値で埋める関数。
    """
    df[column_to_fill] = df.groupby(group_columns)[column_to_fill].transform(lambda x: x.fillna(x.mode()[0] if not x.mode().empty else np.nan))
    return df


# bedrooms列の欠損値を補完
df = fill_missing_with_mode(df, 'bedrooms', ['room_type','accommodates'])

# beds列の欠損値を補完
df = fill_missing_with_mode(df, 'beds', ['room_type','accommodates'])


# 補完後のデータフレームの最初の5行を表示して確認
print(df.head())
#再度欠損値を確認
print(df.isnull().sum())
STEP
修正後の確認

再度、欠損値の有無を確認し、修正が成功したかどうかを確認します。

print(df.isnull().sum())

補足事項

  • 最頻値補完の注意点: 最頻値補完は、データに偏りがある場合に不適切な結果になる可能性があります。今回は該当するデータがないため、問題ありませんが、他のデータセットでは注意が必要です。
  • 欠損値の種類: 今回は単純な欠損値(NaN)でしたが、データによっては「0」や「-1」などの特別な値が欠損を表している場合があります。その場合は、データの意味を理解した上で適切な処理を行う必要があります。
  • fillnaメソッド: pandasのfillnaメソッドを使うことでも欠損値の補完は可能です。平均値や中央値で補完することもできます。

探索的データ分析

STEP
 データの概要を確認

まず、データフレームの基本的な情報を確認します。

print(df.info())
print(df.describe())
STEP
目的変数(y)の分布を確認

y(宿泊価格)の分布を確認します。

import matplotlib.pyplot as plt
import seaborn as sns

plt.figure(figsize=(8, 6))
sns.histplot(df['y'], kde=True)
plt.title('Distribution of Price (y)')
plt.xlabel('Price')
plt.ylabel('Frequency')
plt.show()
STEP
各特徴量と目的変数の関係を分析

次に、各特徴量と目的変数yの関係を分析します。

3-1: 数値データの特徴量

数値データの特徴量(accommodates, bathrooms, number_of_reviews, review_scores_rating, bedrooms, beds)とyの関係を散布図で確認します。

numerical_features = ['accommodates', 'bathrooms', 'number_of_reviews', 'review_scores_rating', 'bedrooms', 'beds']

for feature in numerical_features:
    plt.figure(figsize=(8, 6))
    sns.scatterplot(x=feature, y='y', data=df)
    plt.title(f'{feature} vs Price')
    plt.xlabel(feature)
    plt.ylabel('Price')
    plt.show()

3-2: カテゴリデータの特徴量

カテゴリデータの特徴量(property_type, room_type, bed_type, cleaning_fee, city, host_identity_verified, host_response_rate, instant_bookable)とyの関係を箱ひげ図で確認します。

categorical_features = ['property_type', 'room_type', 'bed_type', 'cleaning_fee', 'city', 'host_identity_verified', 'host_response_rate', 'instant_bookable']


for feature in categorical_features:
    plt.figure(figsize=(10, 6))
    sns.boxplot(x=feature, y='y', data=df)
    plt.title(f'{feature} vs Price')
    plt.xlabel(feature)
    plt.ylabel('Price')
    plt.xticks(rotation=45, ha='right')
    plt.tight_layout()
    plt.show()
STEP
相関行列を確認

数値データ間の相関関係をヒートマップで確認します。

plt.figure(figsize=(10, 8))
sns.heatmap(df[numerical_features + ['y']].corr(), annot=True, cmap='coolwarm')
plt.title('Correlation Matrix')
plt.show()
STEP
考察

ここまでのEDAの結果から、y(宿泊価格)に影響を与える可能性のある特徴量を6つ程度考えます。

  1. accommodates (収容人数): 散布図から、収容人数が増えるほど価格が上がる傾向が見られます。相関も比較的高いため、重要な要素と考えられます。
  2. bedrooms (ベッドルーム数): 散布図から、ベッドルーム数が増えるほど価格が上がる傾向が見られます。相関も比較的高いため、重要な要素と考えられます。
  3. beds (ベッド数): 散布図から、ベッド数が増えるほど価格が上がる傾向が見られます。相関も比較的高いため、重要な要素と考えられます。
  4. room_type (部屋タイプ): 箱ひげ図から、部屋タイプによって価格帯が大きく異なることがわかります。特に「Entire home/apt」は他のタイプよりも高価格帯にあるため、重要な要素と考えられます。
  5. property_type (物件タイプ): 箱ひげ図から、物件タイプによって価格帯が異なることがわかります。特に「Townhouse」や「House」などは高価格帯となる傾向がみられます。
  6. review_scores_rating (レビュー評価): 散布図から、レビュー評価が高いほど価格が上がる傾向が見られます。相関も比較的高いため、重要な要素と考えられます。

これらの特徴量は、宿泊価格を予測する上で重要な役割を果たす可能性があります。もちろん、これ以外にも影響を与える特徴量があるかもしれませんが、まずはこれらの特徴量に着目してモデル構築を進めていくのが良いでしょう。

物件データの加工

STEP
仮説の再確認

まず、y(宿泊価格)に影響を与える可能性が高いと仮定した6つの特徴量を再確認します。

  1. accommodates (収容人数)
  2. bedrooms (ベッドルーム数)
  3. beds (ベッド数)
  4. room_type (部屋タイプ)
  5. property_type (物件タイプ)
  6. review_scores_rating (レビュー評価)
STEP
数値特徴量の加工

まず、数値特徴量 (accommodates, bedrooms, beds, review_scores_rating) について、必要に応じてスケーリングや変換を行います。

from sklearn.preprocessing import StandardScaler

# StandardScaler をインスタンス化
scaler = StandardScaler()

# 数値特徴量を標準化
numerical_features = ['accommodates', 'bedrooms', 'beds', 'review_scores_rating']
df[numerical_features] = scaler.fit_transform(df[numerical_features])

print(df[numerical_features].head())
STEP
カテゴリ特徴量の加工

次に、カテゴリ特徴量 (room_type, property_type) をone-hotエンコーディングで数値に変換します。

# One-Hot Encoding
df = pd.get_dummies(df, columns=['room_type', 'property_type'], drop_first=True)
print(df.head())
STEP
その他の特徴量の加工

cleaning_fee、host_identity_verified、instant_bookableについてもone-hotエンコーディングをします。
host_response_rateは数値データですが、ここではカテゴリデータとして扱い、binningしてからone-hotエンコーディングをします。

# 'cleaning_fee', 'host_identity_verified', 'instant_bookable'のone-hotエンコーディング
df = pd.get_dummies(df, columns=['cleaning_fee', 'host_identity_verified', 'instant_bookable'], drop_first=True)


# 'host_response_rate'をbinning
bins = [0, 70, 80, 90, 100]
labels = ['70%以下', '70%~80%', '80%~90%', '90%以上']
df['host_response_rate_binned'] = pd.cut(df['host_response_rate'].str.replace('%', '').astype(int), bins=bins, labels=labels, right=False)

# 'host_response_rate_binned'のone-hotエンコーディング
df = pd.get_dummies(df, columns=['host_response_rate_binned'], drop_first=True)


# 不要になった'host_response_rate'列を削除
df = df.drop('host_response_rate', axis=1)
print(df.head())
STEP
仮説の検証 (相関の確認)

加工後の特徴量とyとの相関を再度確認し、仮説を検証します。

# 相関行列の確認
features_for_corr = numerical_features + [col for col in df.columns if 'room_type_' in col or 'property_type_' in col or 'cleaning_fee_' in col or 'host_identity_verified_' in col or 'instant_bookable_' in col or 'host_response_rate_binned_' in col ] + ['y']


plt.figure(figsize=(15, 10))
sns.heatmap(df[features_for_corr].corr(), annot=True, cmap='coolwarm')
plt.title('Correlation Matrix After Feature Engineering')
plt.show()
STEP
データ加工後のデータフレームの確認

最後に、加工後のデータフレームの情報を確認します。

print(df.info())
print(df.head())
STEP
不要な特徴量の削除

データ加工の結果、不要になった特徴量(‘id’,’city’,’name’)を削除します。

df = df.drop(['id','city','name'],axis=1)
print(df.head())
  • 数値特徴量のスケーリング: 数値特徴量を標準化することで、スケールが異なっていた特徴量を同じスケールで扱うことができるようになりました。これにより、モデルが特定のスケールの特徴量に偏って学習することを防ぎます。
  • カテゴリ特徴量のOne-Hot Encoding: カテゴリ特徴量をOne-Hot Encodingで数値に変換することで、モデルがこれらの特徴量を扱えるようになりました。これにより、カテゴリ特徴量の情報をモデルの学習に活用できます。
  • 相関の確認: 加工後の特徴量とyの相関を再度確認することで、どの特徴量がyに対してより強く影響を与えるか、より正確に把握できました。
  • その他の特徴量: cleaning_fee、host_identity_verified、instant_bookable、host_response_rateを数値化することでモデルが利用可能になり、より詳細な分析に活用できます。

予測モデルの作成

STEP
データの準備

まず、データを特徴量(X)と目的変数(y)に分割します。

from sklearn.model_selection import train_test_split

# 特徴量と目的変数に分割
X = df.drop('y', axis=1)  # 'y' 列以外を特徴量とする
y = df['y']             # 'y' 列を目的変数とする

# 学習データとテストデータに分割
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

print("X_train shape:", X_train.shape)
print("y_train shape:", y_train.shape)
print("X_test shape:", X_test.shape)
print("y_test shape:", y_test.shape)
STEP
モデルの学習

LinearRegressionモデルを作成し、学習データで学習させます。

from sklearn.linear_model import LinearRegression

# モデルのインスタンス化
model = LinearRegression()

# モデルの学習
model.fit(X_train, y_train)
STEP
モデルの評価

テストデータを使ってモデルの性能を評価します。

from sklearn.metrics import mean_squared_error, r2_score

# テストデータで予測
y_pred = model.predict(X_test)

# モデルの評価
mse = mean_squared_error(y_test, y_pred)
r2 = r2_score(y_test, y_pred)

print(f"Mean Squared Error: {mse:.2f}")
print(f"R-squared: {r2:.2f}")
STEP
モデルの予測

学習したモデルを使って、新しいデータの予測を行います。

# 新しいデータ(例)
new_data = pd.DataFrame({
    'accommodates': [3],
    'bedrooms': [1],
    'beds': [2],
    'review_scores_rating': [4.5],
    'room_type_Private room': [0],
    'room_type_Shared room': [0],
    'property_type_Condominium': [0],
    'property_type_House': [0],
    'property_type_Townhouse': [0],
     'cleaning_fee_TRUE':[1],
    'host_identity_verified_TRUE':[1],
    'instant_bookable_TRUE':[0],
    'host_response_rate_binned_70%~80%':[1],
    'host_response_rate_binned_80%~90%':[0],
    'host_response_rate_binned_90%以上':[0]
})

# 数値特徴量のスケーリングを適用
new_data[numerical_features] = scaler.transform(new_data[numerical_features])

# 予測
predicted_price = model.predict(new_data)
print(f"Predicted price: {predicted_price[0]:.2f}")
STEP
モデルの係数を確認

モデルの学習結果から、各特徴量の係数(重み)を確認します。

# 係数 (重み) を表示
coefficients = pd.DataFrame({'feature': X.columns, 'coefficient': model.coef_})
print(coefficients)
STEP
モデルの評価

テストデータを使って予測を行い、RMSEを計算します。

# テストデータで予測
y_pred = model.predict(X_test)

# 平均二乗誤差(MSE)を計算
mse = mean_squared_error(y_test, y_pred)

# RMSEを計算
rmse = np.sqrt(mse)

print(f"Root Mean Squared Error (RMSE): {rmse:.2f}")

まとめ

  • データの準備: データを特徴量と目的変数に分割し、学習用とテスト用に分けました。
  • モデルの学習: LinearRegression モデルを作成し、学習データを使って学習させました。
  • モデルの評価: テストデータを使ってモデルの性能を評価しました(MSEとR2)。
  • モデルの予測: 学習したモデルを使って、新しいデータの予測を行いました。
  • 係数の確認: モデルの各特徴量に対する係数(重み)を確認しました。

モデルの改善

  1. 多項式特徴量の追加: 特徴量同士の交互作用を捉えるために、多項式特徴量を追加します。
  2. 正則化の導入: 過学習を抑制するために、Ridge回帰を試します。
  3. 交差検証による評価: モデルの汎化性能をより正確に評価するために、交差検証を行います。
STEP
必要なライブラリのインポート

まず、必要なライブラリをインポートします。

import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split, cross_val_score, KFold
from sklearn.preprocessing import StandardScaler, PolynomialFeatures
from sklearn.linear_model import Ridge
from sklearn.metrics import mean_squared_error
STEP
データの準備

データを特徴量(X)と目的変数(y)に分割し、学習データとテストデータに分割します。

# 特徴量と目的変数に分割
X = df.drop('y', axis=1)  # 'y' 列以外を特徴量とする
y = df['y']             # 'y' 列を目的変数とする

# 学習データとテストデータに分割
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
STEP
多項式特徴量の追加

PolynomialFeaturesを使って、多項式特徴量を追加します。

# 多項式特徴量を追加
poly = PolynomialFeatures(degree=2, include_bias=False)
X_train_poly = poly.fit_transform(X_train)
X_test_poly = poly.transform(X_test)

print("Shape of X_train_poly:", X_train_poly.shape)
print("Shape of X_test_poly:", X_test_poly.shape)
STEP
特徴量の標準化

多項式特徴量を追加した後、特徴量を標準化します。

# 特徴量の標準化
scaler = StandardScaler()
X_train_poly_scaled = scaler.fit_transform(X_train_poly)
X_test_poly_scaled = scaler.transform(X_test_poly)
STEP
Ridge回帰モデルの学習

Ridge回帰モデルを作成し、学習データで学習させます。

# Ridge回帰モデルの作成
ridge = Ridge(alpha=1.0)  # alpha は正則化の強さを調整するハイパーパラメータ
ridge.fit(X_train_poly_scaled, y_train)
STEP
モデルの評価 (RMSE)

テストデータを使ってモデルの性能を評価します。

# テストデータで予測
y_pred_ridge = ridge.predict(X_test_poly_scaled)

# 平均二乗誤差(MSE)を計算
mse_ridge = mean_squared_error(y_test, y_pred_ridge)

# RMSEを計算
rmse_ridge = np.sqrt(mse_ridge)

print(f"Root Mean Squared Error (RMSE) with Ridge: {rmse_ridge:.2f}")
STEP
交差検証による評価

交差検証を使って、モデルの汎化性能をより正確に評価します。

# 交差検証の実施
cv = KFold(n_splits=5, shuffle=True, random_state=42)
scores = cross_val_score(ridge, X_train_poly_scaled, y_train, cv=cv, scoring='neg_mean_squared_error')
rmse_scores = np.sqrt(-scores)

print(f"Cross-validation RMSE scores: {rmse_scores}")
print(f"Mean cross-validation RMSE: {rmse_scores.mean():.2f}")
STEP
モデルの予測

学習したモデルを使って、新しいデータの予測を行います。

# 新しいデータ(例)
new_data = pd.DataFrame({
     'accommodates': [3],
    'bedrooms': [1],
    'beds': [2],
    'review_scores_rating': [4.5],
    'room_type_Private room': [0],
    'room_type_Shared room': [0],
    'property_type_Condominium': [0],
    'property_type_House': [0],
    'property_type_Townhouse': [0],
     'cleaning_fee_TRUE':[1],
    'host_identity_verified_TRUE':[1],
    'instant_bookable_TRUE':[0],
    'host_response_rate_binned_70%~80%':[1],
    'host_response_rate_binned_80%~90%':[0],
    'host_response_rate_binned_90%以上':[0]
})
# 新しいデータに多項式特徴量を適用
new_data_poly = poly.transform(new_data)

# スケーリングを適用
new_data_poly_scaled = scaler.transform(new_data_poly)

# 予測
predicted_price = ridge.predict(new_data_poly_scaled)
print(f"Predicted price: {predicted_price[0]:.2f}")
  • 多項式特徴量: 特徴量同士の交互作用を捉えることで、モデルの表現力が向上しました。
  • Ridge回帰: 正則化を導入することで、過学習を抑制し、汎化性能が向上しました。
  • 交差検証: モデルの汎化性能をより正確に評価できるようになりました。
  • RMSE: Ridge回帰を適用することにより、RMSEが改善しました。(改善幅はデータによります。)

この結果を基に、さらにモデルの改善を試みることができます。
例えば、Ridge回帰のハイパーパラメータalphaの調整や、他の正則化手法(Lasso回帰)を試すことも有効です。
また、より複雑なモデル(例:Gradient Boosting Machinesなど)を検討するのも良いでしょう。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

コメント

コメントする

CAPTCHA


目次