Scikit-learn: Tidak bisa mendapatkan nama fitur setelah ColumnTransformer

Dibuat pada 6 Nov 2018  ·  13Komentar  ·  Sumber: scikit-learn/scikit-learn

Ketika saya menggunakan ColumnTransformer untuk memproses kolom yang berbeda (termasuk numerik, kategori, teks) dengan pipa, saya tidak bisa mendapatkan nama fitur dari data yang diubah akhir, yang sulit untuk debugging.

Berikut kodenya:

titanic_url = ('https://raw.githubusercontent.com/amueller/'
               'scipy-2017-sklearn/091d371/notebooks/datasets/titanic3.csv')

data = pd.read_csv(titanic_url)

target = data.pop('survived')

numeric_columns = ['age','sibsp','parch']
category_columns = ['pclass','sex','embarked']
text_columns = ['name','home.dest']

numeric_transformer = Pipeline(steps=[
    ('impute',SimpleImputer(strategy='median')),
    ('scaler',StandardScaler()
    )
])
category_transformer = Pipeline(steps=[
    ('impute',SimpleImputer(strategy='constant',fill_value='missing')),
    ('ohe',OneHotEncoder(handle_unknown='ignore'))
])
text_transformer = Pipeline(steps=[
    ('cntvec',CountVectorizer())
])

preprocesser = ColumnTransformer(transformers=[
    ('numeric',numeric_transformer,numeric_columns),
    ('category',category_transformer,category_columns),
    ('text',text_transformer,text_columns[0])
])

preprocesser.fit_transform(data)
  1. preprocesser.get_feature_names() akan mendapatkan kesalahan:
    AttributeError: Transformer numeric (type Pipeline) does not provide get_feature_names.
  2. Dalam ColumnTransformer text_transformer hanya dapat memproses string (misalnya 'Seks'), tetapi bukan daftar string sebagai text_columns

Komentar yang paling membantu

Ini bukan masalah tentang ColumnTransformer.

  1. adalah tentang Pipa. Perhatikan bahwa eli5 mengimplementasikan fungsi nama fitur yang dapat mendukung Pipeline.

Re 2. mungkin Anda benar bahwa itu tidak ramah bahwa kami tidak memiliki cara yang bersih untuk menerapkan vectorizer teks ke setiap kolom. Saya tidak yakin bagaimana itu dapat dicapai dengan bersih, kecuali jika kita mulai mendukung beberapa kolom input di CountVectorizer dll.

Terima kasih atas balasan Anda yang baik!
Seperti yang saya ketahui, ketika saya melakukan praproses kolom menggunakan metode yang dapat mengubah satu kolom menjadi multi-kolom seperti OneHotEncoder , CountVectorizer , saya bisa mendapatkan nama kolom data baru dari trafo langkah terakhir pipa dengan function get_feature_names , saat menggunakan metode yang tidak membuat kolom baru, cukup atur nama kolom mentah.

def get_column_names_from_ColumnTransformer(column_transformer):    
    col_name = []
    for transformer_in_columns in column_transformer.transformers_[:-1]:#the last transformer is ColumnTransformer's 'remainder'
        raw_col_name = transformer_in_columns[2]
        if isinstance(transformer_in_columns[1],Pipeline): 
            transformer = transformer_in_columns[1].steps[-1][1]
        else:
            transformer = transformer_in_columns[1]
        try:
            names = transformer.get_feature_names()
        except AttributeError: # if no 'get_feature_names' function, use raw column name
            names = raw_col_name
        if isinstance(names,np.ndarray): # eg.
            col_name += names.tolist()
        elif isinstance(names,list):
            col_name += names    
        elif isinstance(names,str):
            col_name.append(names)
    return col_name

Dengan menggunakan kode di atas, saya bisa mendapatkan nama kolom preprocesser .
Apakah kode ini menyelesaikan pertanyaan ini?
Pada eli5, saya tidak menemukan fungsi itu, Bisakah Anda memberi saya tautan untuk contoh eksplisit atau api untuk eli5?

Semua 13 komentar

Ini bukan masalah tentang ColumnTransformer.

  1. adalah tentang Pipa. Perhatikan bahwa eli5 mengimplementasikan fungsi nama fitur yang dapat mendukung Pipeline.

Re 2. mungkin Anda benar bahwa itu tidak ramah bahwa kami tidak memiliki cara yang bersih untuk menerapkan vectorizer teks ke setiap kolom. Saya tidak yakin bagaimana itu dapat dicapai dengan bersih, kecuali jika kita mulai mendukung beberapa kolom input di CountVectorizer dll.

Ini bukan masalah tentang ColumnTransformer.

  1. adalah tentang Pipa. Perhatikan bahwa eli5 mengimplementasikan fungsi nama fitur yang dapat mendukung Pipeline.

Re 2. mungkin Anda benar bahwa itu tidak ramah bahwa kami tidak memiliki cara yang bersih untuk menerapkan vectorizer teks ke setiap kolom. Saya tidak yakin bagaimana itu dapat dicapai dengan bersih, kecuali jika kita mulai mendukung beberapa kolom input di CountVectorizer dll.

Terima kasih atas balasan Anda yang baik!
Seperti yang saya ketahui, ketika saya melakukan praproses kolom menggunakan metode yang dapat mengubah satu kolom menjadi multi-kolom seperti OneHotEncoder , CountVectorizer , saya bisa mendapatkan nama kolom data baru dari trafo langkah terakhir pipa dengan function get_feature_names , saat menggunakan metode yang tidak membuat kolom baru, cukup atur nama kolom mentah.

def get_column_names_from_ColumnTransformer(column_transformer):    
    col_name = []
    for transformer_in_columns in column_transformer.transformers_[:-1]:#the last transformer is ColumnTransformer's 'remainder'
        raw_col_name = transformer_in_columns[2]
        if isinstance(transformer_in_columns[1],Pipeline): 
            transformer = transformer_in_columns[1].steps[-1][1]
        else:
            transformer = transformer_in_columns[1]
        try:
            names = transformer.get_feature_names()
        except AttributeError: # if no 'get_feature_names' function, use raw column name
            names = raw_col_name
        if isinstance(names,np.ndarray): # eg.
            col_name += names.tolist()
        elif isinstance(names,list):
            col_name += names    
        elif isinstance(names,str):
            col_name.append(names)
    return col_name

Dengan menggunakan kode di atas, saya bisa mendapatkan nama kolom preprocesser .
Apakah kode ini menyelesaikan pertanyaan ini?
Pada eli5, saya tidak menemukan fungsi itu, Bisakah Anda memberi saya tautan untuk contoh eksplisit atau api untuk eli5?

Sehubungan dengan eli5, lihat transform_feature_names (digunakan oleh explain_weights)

1 adalah duplikat dari #6425, kan? Saya ingin menulis tentang itu.
Saya pikir mendukung banyak kolom teks cukup mudah dengan ColumnTransformer . Ini bukan kode yang paling cantik tetapi Anda bisa menambahkan CountVectorizer untuk setiap kolom teks.

Dan cuplikan Anda tidak benar-benar menyelesaikan masalah karena tidak ada get_feature_names tidak berarti Anda bisa menggunakan nama kolom saja.

1 adalah duplikat dari #6425, kan? Saya ingin menulis tentang itu.
Saya pikir mendukung banyak kolom teks cukup mudah dengan ColumnTransformer . Ini bukan kode yang paling cantik tetapi Anda bisa menambahkan CountVectorizer untuk setiap kolom teks.

Dan cuplikan Anda tidak benar-benar menyelesaikan masalah karena tidak ada get_feature_names tidak berarti Anda bisa menggunakan nama kolom saja.

ya, setelah dataFrame panda dimasukkan ke dalam pipa praproses, Lebih baik mendapatkan nama fitur sehingga bisa tahu persis apa yang terjadi hanya dari data yang dihasilkan.

ok, tutup sebagai duplikat.

Ini bukan masalah tentang ColumnTransformer.

  1. adalah tentang Pipa. Perhatikan bahwa eli5 mengimplementasikan fungsi nama fitur yang dapat mendukung Pipeline.

Re 2. mungkin Anda benar bahwa itu tidak ramah bahwa kami tidak memiliki cara yang bersih untuk menerapkan vectorizer teks ke setiap kolom. Saya tidak yakin bagaimana itu dapat dicapai dengan bersih, kecuali jika kita mulai mendukung beberapa kolom input di CountVectorizer dll.

Terima kasih atas balasan Anda yang baik!
Seperti yang saya ketahui, ketika saya melakukan praproses kolom menggunakan metode yang dapat mengubah satu kolom menjadi multi-kolom seperti OneHotEncoder , CountVectorizer , saya bisa mendapatkan nama kolom data baru dari trafo langkah terakhir pipa dengan function get_feature_names , saat menggunakan metode yang tidak membuat kolom baru, cukup atur nama kolom mentah.

def get_column_names_from_ColumnTransformer(column_transformer):    
    col_name = []
    for transformer_in_columns in column_transformer.transformers_[:-1]:#the last transformer is ColumnTransformer's 'remainder'
        raw_col_name = transformer_in_columns[2]
        if isinstance(transformer_in_columns[1],Pipeline): 
            transformer = transformer_in_columns[1].steps[-1][1]
        else:
            transformer = transformer_in_columns[1]
        try:
            names = transformer.get_feature_names()
        except AttributeError: # if no 'get_feature_names' function, use raw column name
            names = raw_col_name
        if isinstance(names,np.ndarray): # eg.
            col_name += names.tolist()
        elif isinstance(names,list):
            col_name += names    
        elif isinstance(names,str):
            col_name.append(names)
    return col_name

Dengan menggunakan kode di atas, saya bisa mendapatkan nama kolom preprocesser .
Apakah kode ini menyelesaikan pertanyaan ini?
Pada eli5, saya tidak menemukan fungsi itu, Bisakah Anda memberi saya tautan untuk contoh eksplisit atau api untuk eli5?

Saya membuat peningkatan kecil untuk mendapatkan kembali nama seperti rawname_value untuk formulir onehot:

def get_column_names_from_ColumnTransformer(column_transformer):    
    col_name = []
    for transformer_in_columns in column_transformer.transformers_[:-1]:#the last transformer is ColumnTransformer's 'remainder'
        raw_col_name = transformer_in_columns[2]
        raw_col_name_reverse = raw_col_name[::-1]
        if isinstance(transformer_in_columns[1],Pipeline): 
            transformer = transformer_in_columns[1].steps[-1][1]
        else:
            transformer = transformer_in_columns[1]
        try:
            names = transformer.get_feature_names()
            exchange_name = [(_.split("_")) for _ in preprocessor.transformers_[:-1][0][1].steps[-1][1].get_feature_names()]
            last_pre_name = ""
            last_raw_name = ""
            for pre_name,value in exchange_name:
                if pre_name==last_pre_name:
                    col_name.append(last_raw_name+"_"+value)
                if pre_name!=last_pre_name:
                    last_pre_name=pre_name
                    last_raw_name=raw_col_name_reverse.pop()
                    col_name.append(last_raw_name+"_"+value)
        except AttributeError: # if no 'get_feature_names' function, use raw column name
            names = raw_col_name
        if isinstance(names,np.ndarray): # eg.
            col_name += names.tolist()
        elif isinstance(names,list):
            col_name += names    
        elif isinstance(names,str):
            col_name.append(names)
    return col_name

Ini bukan masalah tentang ColumnTransformer.

  1. adalah tentang Pipa. Perhatikan bahwa eli5 mengimplementasikan fungsi nama fitur yang dapat mendukung Pipeline.

Re 2. mungkin Anda benar bahwa itu tidak ramah bahwa kami tidak memiliki cara yang bersih untuk menerapkan vectorizer teks ke setiap kolom. Saya tidak yakin bagaimana itu dapat dicapai dengan bersih, kecuali jika kita mulai mendukung beberapa kolom input di CountVectorizer dll.

Terima kasih atas balasan Anda yang baik!
Seperti yang saya ketahui, ketika saya melakukan praproses kolom menggunakan metode yang dapat mengubah satu kolom menjadi multi-kolom seperti OneHotEncoder , CountVectorizer , saya bisa mendapatkan nama kolom data baru dari trafo langkah terakhir pipa dengan function get_feature_names , saat menggunakan metode yang tidak membuat kolom baru, cukup atur nama kolom mentah.

def get_column_names_from_ColumnTransformer(column_transformer):    
    col_name = []
    for transformer_in_columns in column_transformer.transformers_[:-1]:#the last transformer is ColumnTransformer's 'remainder'
        raw_col_name = transformer_in_columns[2]
        if isinstance(transformer_in_columns[1],Pipeline): 
            transformer = transformer_in_columns[1].steps[-1][1]
        else:
            transformer = transformer_in_columns[1]
        try:
            names = transformer.get_feature_names()
        except AttributeError: # if no 'get_feature_names' function, use raw column name
            names = raw_col_name
        if isinstance(names,np.ndarray): # eg.
            col_name += names.tolist()
        elif isinstance(names,list):
            col_name += names    
        elif isinstance(names,str):
            col_name.append(names)
    return col_name

Dengan menggunakan kode di atas, saya bisa mendapatkan nama kolom preprocesser .
Apakah kode ini menyelesaikan pertanyaan ini?
Pada eli5, saya tidak menemukan fungsi itu, Bisakah Anda memberi saya tautan untuk contoh eksplisit atau api untuk eli5?

Bagaimana jika Anda menerapkan simpleimputer dengan add_indicator dalam pipa? Pendekatan ini tidak akan berhasil.

Bagaimana jika Anda menerapkan simpleimputer dengan add_indicator dalam pipa? Pendekatan ini tidak akan berhasil.

Akan menyenangkan untuk memiliki metode get_feature_names untuk konfigurasi ini.

Bagaimana jika Anda menerapkan simpleimputer dengan add_indicator dalam pipa? Pendekatan ini tidak akan berhasil.

Inilah kontribusi saya untuk solusi jangka pendek. Ini memaksa semua jenis array yang berbeda ke daftar, dan menangani kasus SimpleImputer(add_indicate=True). Ini juga sedikit lebih bertele-tele.

def get_column_names_from_ColumnTransformer(column_transformer):    
    col_name = []

    for transformer_in_columns in column_transformer.transformers_[:-1]: #the last transformer is ColumnTransformer's 'remainder'
        print('\n\ntransformer: ', transformer_in_columns[0])

        raw_col_name = list(transformer_in_columns[2])

        if isinstance(transformer_in_columns[1], Pipeline): 
            # if pipeline, get the last transformer
            transformer = transformer_in_columns[1].steps[-1][1]
        else:
            transformer = transformer_in_columns[1]

        try:
          if isinstance(transformer, OneHotEncoder):
            names = list(transformer.get_feature_names(raw_col_name))

          elif isinstance(transformer, SimpleImputer) and transformer.add_indicator:
            missing_indicator_indices = transformer.indicator_.features_
            missing_indicators = [raw_col_name[idx] + '_missing_flag' for idx in missing_indicator_indices]

            names = raw_col_name + missing_indicators

          else:
            names = list(transformer.get_feature_names())

        except AttributeError as error:
          names = raw_col_name

        print(names)    

        col_name.extend(names)

    return col_name

FYI, saya menulis beberapa kode dan blog tentang cara mengekstrak nama fitur dari Pipelines & ColumnTransformers yang kompleks. Kode ini merupakan peningkatan dari posting saya sebelumnya. https://towardsdatascience.com/extracting-plotting-feature-names-importance-from-scikit-learn-pipelines-eb5bfa6a31f4

@kylegilde Artikel yang bagus dan terima kasih untuk kodenya. Bekerja seperti pesona. Untuk penjelasan global, saya telah bergulat dengan KernelSHAP dan alibi selama beberapa jam tetapi transformator onehot saya tidak berfungsi tanpa handle_unkown='ignore'

Berikut adalah versi lain dari cuplikan @pjgao yang menyertakan kolom dari pengingat:

def get_columns_from_transformer(column_transformer, input_colums):    
    col_name = []

    for transformer_in_columns in column_transformer.transformers_[:-1]: #the last transformer is ColumnTransformer's 'remainder'
        raw_col_name = transformer_in_columns[2]
        if isinstance(transformer_in_columns[1],Pipeline): 
            transformer = transformer_in_columns[1].steps[-1][1]
        else:
            transformer = transformer_in_columns[1]
        try:
            names = transformer.get_feature_names(raw_col_name)
        except AttributeError: # if no 'get_feature_names' function, use raw column name
            names = raw_col_name
        if isinstance(names,np.ndarray): # eg.
            col_name += names.tolist()
        elif isinstance(names,list):
            col_name += names    
        elif isinstance(names,str):
            col_name.append(names)

    [_, _, reminder_columns] = column_transformer.transformers_[-1]

    for col_idx in reminder_columns:
        col_name.append(input_colums[col_idx])

    return col_name

Apa pendapat Anda tentang menambahkan fungsi serupa ke basis kode inti?

Apakah halaman ini membantu?
0 / 5 - 0 peringkat