В качестве упражнения я пытаюсь создать собственный преобразователь, который принимает набор данных и метки и возвращает преобразованный набор данных, сохраняя только те столбцы, корреляция которых с метками превышает определенный порог. Трансформатор задается следующим кодом:
from sklearn.base import BaseEstimator, TransformerMixin
class CorrelatedAttributesKeeper(BaseEstimator, TransformerMixin):
def __init__(self, correlation_threshold = 0.5):
self.correlation_threshold = correlation_threshold
self.returned_indices = []
def fit(self, X, y=None):
correlations = []
for col in X:
correlations.append(np.corrcoef(X[col].to_numpy(), y.to_numpy())[0,1])
for idx, x in enumerate(correlations):
if x > self.correlation_threshold:
self.returned_indices.append(idx)
return self
def transform(self, X):
return X.iloc[:, self.returned_indices]
Кажется, что следующее работает так, как задумано:
high_correl_transformer = CorrelatedAttributesKeeper(0.5)
transformed_housing_num = high_correl_transformer.fit_transform(housing_num, housing_labels)
Однако при попытке запустить его как часть конвейера возникают ошибки:
num_pipeline2 = Pipeline([
('imputer', SimpleImputer(strategy = "median")),
('attribs_adder', CombinedAttributesAdder()),
('std_scaler', StandardScaler()),
('correl_keeper', CorrelatedAttributesKeeper()),])
housing_num_tr2 = num_pipeline.fit_transform(housing_num, housing_labels)
Это приводит к следующей ошибке
Traceback (most recent call last):
File "<string>", line 17, in __PYTHON_EL_eval
File "<string>", line 3, in <module>
File "/tmp/babel-0JUX9y/python-okiPo8", line 6, in <module>
housing_num_tr2 = num_pipeline.fit_transform(housing_num, housing_labels)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.11/site-packages/sklearn/base.py", line 1474, in wrapper
return fit_method(estimator, *args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.11/site-packages/sklearn/pipeline.py", line 543, in fit_transform
return last_step.fit_transform(
^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.11/site-packages/sklearn/utils/_set_output.py", line 295, in wrapped
data_to_wrap = f(self, X, *args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.11/site-packages/sklearn/base.py", line 1101, in fit_transform
return self.fit(X, y, **fit_params).transform(X)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: CorrelatedAttributesKeeper.fit() takes 1 positional argument but 3 were given
Я изо всех сил пытаюсь понять трассировку стека. В нем говорится, что функция fit требует 1 позиционный аргумент, но кажется, что для этого требуется как минимум 3, как в моем определении, так и в нижней части трассировки стека: self, X и y. Чего я не получаю?
Спасибо
Я не знаю, что является причиной конкретного TypeError
, о котором вы сообщили, но этот ответ решает еще одну проблему с классом.
Шаги, предшествующие вашему пользовательскому преобразователю, возвращают массивы numpy
, что может привести к ошибке пользовательского преобразователя, поскольку он ожидает фрейм данных pandas
.
Я собирался предложить использовать StandardScaler().set_output(transform='pandas')
и SimpleImputer().set_output(transform='pandas')
, чтобы настроить их для возврата кадров данных, однако я думаю, что это приведет к ошибке другого пользовательского оценщика, поскольку он ожидает массивы numpy
(я сослался на github примера, который вы работать с).
Вы можете изменить свой собственный преобразователь для работы с массивами numpy
. Я сделал это ниже, и это работает при запуске:
make_pipeline(
SimpleImputer(),
StandardScaler(),
CorrelatedAttributesKeeper()
).fit_transform(X, y)
Я также внес некоторые другие изменения, чтобы сделать его более соответствующим требованиям sklearn
оценщиков. Эти изменения включают добавление завершающего подчеркивания для обозначения соответствующих атрибутов; определяющий self.n_features_in_
; и не создавать новые переменные внутри __init__
(ограничьтесь только сохранением предоставленных аргументов).
Модифицированный класс:
import pandas as pd
import numpy as np
from sklearn.base import BaseEstimator, TransformerMixin
from sklearn.utils import check_array, check_X_y
class CorrelatedAttributesKeeper(BaseEstimator, TransformerMixin):
def __init__(self, correlation_threshold=0.5):
self.correlation_threshold = correlation_threshold
def fit(self, X, y=None):
#To np if necessary, and checks
X, y = check_X_y(X, y)
self.n_features_in_ = X.shape[1]
self.returned_indices_ = []
correlations = []
for col_idx in range(self.n_features_in_):
correlations.append(np.corrcoef(X[:, col_idx], y)[0,1])
for idx, x in enumerate(correlations):
if x > self.correlation_threshold:
self.returned_indices_.append(idx)
return self
def transform(self, X):
X = check_array(X)
return X[:, self.returned_indices_]
Данные и тесты:
#
#Test data
#
np.random.seed(0)
X = pd.DataFrame({
'f0': np.linspace(0, 1, 100),
'f1': np.linspace(0, 1, 100) + np.random.uniform(-0.4, .4, size=100),
'f2': np.random.normal(size=100),
'f3': np.random.normal(size=100),
})
y = pd.Series(np.linspace(0, 1, 100))
# Estimator should return f0 and f1, but not f2 or f3
display(
'Correlation values',
pd.concat([X, y.to_frame()], axis=1).corr().iloc[:-1, -1]
)
#Test separately
#with dataframes
CorrelatedAttributesKeeper().fit_transform(X, y)#.returned_indices_
#with numpy arrays
CorrelatedAttributesKeeper().fit_transform(X.values, y.values)#.returned_indices_
#Test in pipeline
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.impute import SimpleImputer
make_pipeline(
SimpleImputer(),
StandardScaler(),
CorrelatedAttributesKeeper()
).fit_transform(X, y)
Хорошая точка зрения. Я просмотрел это еще раз и не могу отследить источник оригинала TypeError
, поэтому пояснил, что мой ответ касается другой проблемы с классом.
Это хорошая точка. Но я не понимаю, как это могло привести к ошибке в вопросе?