2017-10-04 3 views
3

パイプラインのデータを消去するために、sklearnに一連のカスタム変換を書きます。各カスタム変換では、fittransformのパラメータとして2つのPandas DataFrameが使用され、transformは2つのデータフレームも返します(下記の例を参照)。 DataFrames inとDataFrames out:パイプラインにトランスフォーマが1つしかない場合、これはうまく動作します。このような両方のRransformersがパイプラインに結合されるが sklearnのどのデータが変換されますか?

、:不思議な入力としてタプルを受け取るRemoveMissingRowsBasedOnTarget

pipeline = Pipeline ([ 
     ('remove_missing_columns', RemoveAllMissing (['mailing_address_str_number'])), 
     ('remove_rows_based_on_target', RemoveMissingRowsBasedOnTarget()), 
     ]) 

X, y = pipeline.fit_transform (X, y) 

==>TypeError: tuple indices must be integers or slices, not Series 

クラス。私はこの

pipeline = Pipeline ([ 
     ('remove_rows_based_on_target', RemoveMissingRowsBasedOnTarget()), 
     ('remove_missing_columns', RemoveAllMissing (['mailing_address_str_number'])), 
     ]) 

==> AttributeError: 'tuple' object has no attribute 'apply' 

のような変圧器の位置を切り替えるとエラーがクラスRemoveAllMissingで発生します。どちらの場合も、エラーが発生した行の上に==>というエラーメッセージが表示されます。私は正確に何が起こっているのか正確に何かを読んだことがあると思うが、私はこの問題について何も見つけることができなかった。誰かが私が間違っていることを教えてもらえますか?以下は、問題のコードを分離したものです。

import numpy as np 
import pandas as pd 
import random 
from sklearn.base import BaseEstimator, TransformerMixin 
from sklearn.pipeline import Pipeline 

def create_data (rows, cols, frac_nan, random_state=42): 
    random.seed (random_state) 
    X = pd.DataFrame (np.zeros ((rows, cols)), 
         columns=['col' + str(i) for i in range (cols)], 
         index=None) 
    # Create dataframe of (rows * cols) with random floating points 
    y = pd.DataFrame (np.zeros ((rows,))) 
    for row in range(rows): 
     for col in range(cols): 
      X.iloc [row,col] = random.random() 
     X.iloc [row,1] = np.nan # column 1 exists colely of NaN's 
     y.iloc [row] = random.randint (0, 1) 
    # Assign NaN's to a fraction of X 
    n = int(frac_nan * rows * cols) 
    for i in range (n): 
     row = random.randint (0, rows-1) 
     col = random.randint (0, cols-1) 
     X.iloc [row, col] = np.nan 
    # Same applies to y 
    n = int(frac_nan * rows) 
    for i in range (n): 
     row = random.randint (0, rows-1) 
     y.iloc [row,] = np.nan 

    return X, y  

class RemoveAllMissing (BaseEstimator, TransformerMixin): 
    # remove columns containg NaN only 
    def __init__ (self, requested_cols=[]): 
     self.all_missing_data = requested_cols 

    def fit (self, X, y=None): 
     # find empty columns == columns with all missing data 
     missing_cols = X.apply (lambda x: x.count(), axis=0) 
     for idx in missing_cols.index: 
      if missing_cols [idx] == 0: 
       self.all_missing_data.append (idx) 

     return self 

    def transform (self, X, y=None): 
     print (">RemoveAllMissing - X shape: " + str (X.shape), " y shape: " + str (y.shape), 'type (X):', type(X)) 
     for all_missing_predictor in self.all_missing_data: 
      del X [all_missing_predictor] 

     print ("<RemoveAllMissing - X shape: " + str (X.shape), " y shape: " + str (y.shape), 'type (X):', type(X)) 
     return X, y 

    def fit_transform (self, X, y=None): 
     return self.fit (X, y).transform (X, y) 

class RemoveMissingRowsBasedOnTarget (BaseEstimator, TransformerMixin): 
    # remove each row where target contains one or more NaN's 
    def __init__ (self): 
     self.missing_rows = [] 

    def fit (self, X, y = None): 
     # remove all rows where the target value is missing data 
     print (type (X)) 
     if y is None: 
      print ('RemoveMissingRowsBasedOnTarget: target (y) cannot be None') 
      return self 

     self.missing_rows = np.array (y.notnull()) # false = missing data 

     return self 

    def transform (self, X, y=None): 
     print (">RemoveMissingRowsBasedOnTarget - X shape: " + str (X.shape), " y shape: " + str (y.shape), 'type (X):', type(X)) 
     if y is None: 
      print ('RemoveMissingRowsBasedOnTarget: target (y) cannot be None') 
      return X, y 

     X = X [self.missing_rows].reset_index() 
     del X ['index'] 
     y = y [self.missing_rows].reset_index() 
     del y ['index'] 

     print ("<RemoveMissingRowsBasedOnTarget - X shape: " + str (X.shape), " y shape: " + str (y.shape), 'type (X):', type(X)) 
     return X, y 

    def fit_transform (self, X, y=None): 
     return self.fit (X, y).transform (X, y) 

pipeline = Pipeline ([ 
     ('RemoveAllMissing', RemoveAllMissing()), 
     ('RemoveMissingRowsBasedOnTarget', RemoveMissingRowsBasedOnTarget()), 
     ]) 

X, y = create_data (25, 10, 0.1) 
print ("X shape: " + str (X.shape), " y shape: " + str (y.shape), 'type (X):', type(X)) 
X, y = pipeline.fit_transform (X, y) 
#X, y = RemoveAllMissing().fit_transform (X, y) 
#X, y = RemoveMissingRowsBasedOnTarget().fit_transform (X, y) 

@Vivekとして編集は、私は問題が特定されると実行されますが、スタンドアロンコードによって、元のコードを交換した要求しました。タプルはDataFrameではなくパラメータとして転送されるため、コードはそのままの状態でクラッシュします。パイプラインによってデータ型が変更され、ドキュメントにそのデータ型が見つかりません。 1は、パイプラインへの呼び出しをコメントアウトし、このように、正常に動作しますeveryting変圧器の別の呼び出しの前にコメントを削除する場合:

#X, y = pipeline.fit_transform (X, y) 
X, y = RemoveAllMissing().fit_transform (X, y) 
X, y = RemoveMissingRowsBasedOnTarget().fit_transform (X, y) 
+0

:また、私はあなたが見てすることができますパイプライン内のターゲット変数をy変換するためのいくつかの関連する問題があることがわかりましたprint(type(X)) 'その時点で印刷しますか?(クラス 'RemoveMissingRowsBasedOnTarget'、最初に呼び出されたとき) ' X'は次のクラス( 'RemoveAllMissing')を呼び出すためにDataFrameでなければならないが、その時点ではタプルになったようだ... – Eskapp

+0

それは依存する呼び出しの順序:RemoveMissingRowsBasedOnTargetが最初に呼び出されるとDataFrameが印刷され、2番目に呼び出されるとタプルが印刷されます。エラーメッセージは、rfeferredメソッドを持たないタプルについても不平を言っています。 – Arnold

+0

サンプルデータと一緒にコードを簡単にコピーしてください。 –

答えて

2

[OK]を、今私はあなたのクラスであると思われるエラーを、持っていますパイプラインはyの入力を受け取り、内部のトランスに沿ってそれを渡すことができますが、yは全体を通して一定であり、任意のtransform()メソッドによって返されることはないと仮定します。あなたのコードではそうではありません。その部分を別の場所に分けることができれば、それは機能します。

this line in the source code of pipelineを見る

if hasattr(transformer, 'fit_transform'): 
     res = transformer.fit_transform(X, y, **fit_params) 
    else: 
     res = transformer.fit(X, y, **fit_params).transform(X) 

次の2つの値(X、Y)を返すが、その単一可変resに含まれ、したがって、それはタプルになるれます。それはあなたの次の変圧器で失敗します。

あなたは、このようなY、Xにタプルを解凍することによって、そのようなデータを扱うことができます。

class RemoveMissingRowsBasedOnTarget (BaseEstimator, TransformerMixin): 
    ... 
    ... 

    def fit (self, X, y = None): 
     # remove all rows where the target value is missing data 
     print (type (X)) 
     if isinstance(X, tuple): 
      y=X[1] 
      X=X[0] 

     ... 
     ... 

     return self 

    def transform (self, X, y=None): 
     if isinstance(X, tuple): 
      y=X[1] 
      X=X[0] 

     ... 
     ... 

     return X, y 

    def fit_transform(self, X, y=None): 
     self.fit(X, y).transform(X, y) 

はあなたがパイプライン内のすべての後続の変圧器のためにこれを行うことを確認してください。しかし、XとYの処理を分けることをお勧めします。 `何

+0

これは確かに動作します、ありがとうございます!私のコードで何が間違っているのか分かりました.XとXの両方を送信するのではなく、Xを送信するという決定だけが表示されます。特に問題はありません。特に行単位の操作を実行する場合は、XとYの両方で実行するといいでしょう。これはそのままで、解決策を提供してくれてありがとうございます。 – Arnold

関連する問題