Matplotlib рисует линию регрессора в 3d

#python #pandas #matplotlib

#python #панды #matplotlib

Вопрос:

Итак, я пытаюсь нарисовать линию в 3d, но у меня ничего не получается. Я могу сделать правильный разброс, как вы можете видеть на этом рисунке. разброс

Когда я на самом деле провожу через нее линию, я получаю следующую ошибку: «Ошибка значения: входной операнд имеет больше измерений, чем разрешено переназначением оси»

Есть идеи, как это решить?

Мой код:

 from pathlib import Path

import pandas as pd
from matplotlib import pyplot as plt
from sklearn import linear_model
from sklearn.metrics import r2_score
from sklearn.model_selection import train_test_split

# Path of file
path = Path("data/houses.csv")

df = pd.read_csv(path)


# Assign X and Y axis
X = df[['GarageArea', 'YearBuilt']].apply(pd.to_numeric)
y = df[['SalePrice']].apply(pd.to_numeric)

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.4, random_state=1)

model = linear_model.LinearRegression(fit_intercept=1)
model.fit(X_train, y_train)

y_pred = model.predict(X_test)

print(r2_score(y_test, y_pred))
print(X_test['GarageArea'])

fig = plt.figure(figsize=(10, 8))
ax = fig.add_subplot(111, projection='3d')
ax.scatter(X['GarageArea'], X['YearBuilt'], y, c='blue', marker='o')
ax.plot(X_test['GarageArea'], X_test['YearBuilt'], y_pred, color='blue', linewidth=3)
# set your labels
ax.set_xlabel('Garage Area')
ax.set_ylabel('Year Built')
ax.set_zlabel('Price')

plt.show()
 

Ответ №1:

Вы должны предоставить минимальный рабочий пример путем загрузки data/house.csv , а также предоставить полный стек трассировки ошибки.

Сообщение об ошибке, которое вы видите, должно исходить из ax.plot , а точнее, из строки

 zs = np.broadcast_to(zs, np.shape(xs))
 

это строка 1570 mpl_toolkits/mplot3d/axes3d.py .

X_test['GarageArea'] и X_test['YearBuilt'] представляют собой две серии pandas, что означает, что они представляют собой одномерный массив и имеют форму (N,). y_predict является результатом model.predict . sklearn.linear_model.LinearRegression способен обучаться нескольким целям одновременно, хотя в вашем примере у вас есть только одна цель, которая является ценой продажи, поэтому ее вывод представляет собой матрицу формы (n_samples, n_targets) или (N, 1), что-то вроде [[1],[2],[3],...] .

Axes3D.plot может принимать входные данные (xs, ys, zs, …). По какой-то причине автор Axes3D.plot решил, что zs должен быть

  • либо одинаковой формы xs и ys,
  • или одно значение

таким образом, тот же z будет связан со всеми (xs, ys) и np.broadcast_to используется для обработки обоих случаев. В первом случае broadcast_to это не вступает в силу, а во втором случае будет создан массив одинаковой формы xs и ys со значением zs.

В вашем коде numpy попытается передать (N,1) в (N,). (N,1) означает, что это массив длиной N из массива длиной 1, например [[1],[2],[3]] . broadcast_to аналогично repeat , и невозможно повторить (N,1), чтобы получить (N,) .

Вы можете попробовать еще несколько тестов, чтобы понять правило np.broadcast_to :

НЕТ тест Статус
01 np.broadcast_to(1,np.shape([1,2,3,4,5,6])) ОК
02 np.broadcast_to([1],np.shape([1,2,3,4,5,6])) ОК
03 np.broadcast_to([[1]],np.shape([1,2,3,4,5,6])) ошибка
04 np.broadcast_to([1,2,3],np.shape([1,2,3,4,5,6])) ошибка
05 np.broadcast_to([1,2,3,4,5,6],np.shape([1,2,3,4,5,6])) ОК
06 np.broadcast_to([[1,2,3,4,5,6]],np.shape([1,2,3,4,5,6])) ошибка
07 np.broadcast_to(1,np.shape([[1,2,3],[4,5,6]])) ОК
08 np.broadcast_to([1],np.shape([[1,2,3],[4,5,6]])) ОК
09 np.broadcast_to([[1]],np.shape([[1,2,3],[4,5,6]])) ОК
10 np.broadcast_to([1,2,3],np.shape([[1,2,3],[4,5,6]])) ОК
11 np.broadcast_to([[1,2,3]],np.shape([[1,2,3],[4,5,6]])) ОК
12 np.broadcast_to([[1],[2],[3]],np.shape([[1,2,3],[4,5,6]])) ошибка
13 np.broadcast_to([1,2],np.shape([[1,2,3],[4,5,6]])) ошибка
14 np.broadcast_to([1,2,3,4],np.shape([[1,2,3],[4,5,6]])) ошибка

Чтобы решить вашу проблему, измените y_pred (случай 6) на y_pred.flatten() (случай 5) ax.plot .

Прикрепленный MWE:

 import numpy as np
import pandas as pd
from sklearn import linear_model
from sklearn.metrics import r2_score
from sklearn.model_selection import train_test_split

rng = np.random.default_rng()
N = 2000
yearBuilt = rng.random(N)*(2020-1860) 1860
garageArea = rng.random(N)*1000
salePrice = rng.normal(yearBuilt*1000   garageArea*500   (yearBuilt-1860)*garageArea/(2020-1860)/1000*300000,50000,N)
data = {
    'YearBuilt': yearBuilt,
    'GarageArea': garageArea,
    'SalePrice': salePrice
}
df = pd.DataFrame(data)

# Assign X and Y axis
X = df[['GarageArea', 'YearBuilt']].apply(pd.to_numeric)
y = df[['SalePrice']].apply(pd.to_numeric)

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.4, random_state=1)

model = linear_model.LinearRegression(fit_intercept=1)
model.fit(X_train, y_train)

y_pred = model.predict(X_test)

fig = plt.figure(figsize=(10, 8))
ax = fig.add_subplot(111, projection='3d')
ax.scatter(X['GarageArea'], X['YearBuilt'], y, c='blue', marker='o')
ax.plot(X_test['GarageArea'], X_test['YearBuilt'], y_pred.flatten(), color='red', linewidth=3)
# set your labels
ax.set_xlabel('Garage Area')
ax.set_ylabel('Year Built')
ax.set_zlabel('Price')

plt.show()
 

Смотрите выходное изображение MWE

Представьте, что вы рисуете свои объекты в декартовой системе координат x-y-z. model.predict может рассматриваться как функция, которая отображает массив (GarageArea, YearBuild) другого массива. X_test представляет собой набор случайно выбранных и упорядоченных точек на плоскости xy и model.predict вычисляет координату z. То, что вы видите, будет набором случайных упорядоченных точек, соединенных, как клубок пряжи.

Чтобы нарисовать линию, отражающую линейность вашей модели, вам необходимо предоставить проекцию линии на плоскость xy в качестве входных model.predict данных вместо X_test . Вы можете использовать линейную регрессию GarageArea как функцию YearBuilt, чтобы получить эту строку. Другая идея состоит в том, чтобы нарисовать линейную плоскость прогнозирования или плоскость тренда, которая является более точной, а не линию тренда.