Scikit-learn: لا يمكن الحصول على أسماء المعالم بعد ColumnTransformer

تم إنشاؤها على ٦ نوفمبر ٢٠١٨  ·  13تعليقات  ·  مصدر: scikit-learn/scikit-learn

عندما أستخدم ColumnTransformer لمعالجة الأعمدة المختلفة (بما في ذلك الأرقام والفئة والنص) مع خط الأنابيب ، لا يمكنني الحصول على أسماء الميزات للبيانات المحولة النهائية ، والتي يصعب تصحيحها.

ها هو الكود:

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() :
    AttributeError: Transformer numeric (type Pipeline) does not provide get_feature_names.
  2. في ColumnTransformertext_transformer يمكن فقط معالجة سلسلة (مثل "Sex") ، لكن ليس قائمة سلسلة مثل text_columns

التعليق الأكثر فائدة

هذه ليست مشكلة حول ColumnTransformer.

  1. هو عن خط الأنابيب. لاحظ أن eli5 بتنفيذ وظيفة أسماء الميزات التي يمكن أن تدعم خط الأنابيب.

إعادة 2. ربما تكون محقًا في أنه من غير الود أنه ليس لدينا طريقة نظيفة لتطبيق ناقل النص على كل عمود. لست متأكدًا من كيفية تحقيق ذلك بشكل نظيف ، إلا إذا بدأنا ببساطة في دعم عدة أعمدة من المدخلات في CountVectorizer وما إلى ذلك.

شكرا لردك اللطيف!
كما أعلم ، عندما أعالج عمودًا مسبقًا باستخدام طرق يمكنها تغيير عمود واحد إلى أعمدة متعددة مثل OneHotEncoder ، CountVectorizer ، يمكنني الحصول على أسماء أعمدة البيانات الجديدة من محول الخطوة الأخيرة في خط الأنابيب عن طريق الوظيفة get_feature_names ، عند استخدام الطرق التي لا تنشئ أعمدة جديدة ، يمكنك فقط تعيين اسم الأعمدة الأولية.

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

باستخدام الرمز أعلاه ، يمكنني الحصول على أسماء أعمدة preprocesser الخاصة بي.
هل هذه الشفرة تحل هذا السؤال؟
اعتبارًا من eli5 ، لا أجد هذه الوظيفة ، هل يمكنك إعطائي رابطًا للمثال الصريح أو api لـ eli5؟

ال 13 كومينتر

هذه ليست مشكلة حول ColumnTransformer.

  1. هو عن خط الأنابيب. لاحظ أن eli5 بتنفيذ وظيفة أسماء الميزات التي يمكن أن تدعم خط الأنابيب.

إعادة 2. ربما تكون محقًا في أنه من غير الود أنه ليس لدينا طريقة نظيفة لتطبيق ناقل النص على كل عمود. لست متأكدًا من كيفية تحقيق ذلك بشكل نظيف ، إلا إذا بدأنا ببساطة في دعم عدة أعمدة من المدخلات في CountVectorizer وما إلى ذلك.

هذه ليست مشكلة حول ColumnTransformer.

  1. هو عن خط الأنابيب. لاحظ أن eli5 بتنفيذ وظيفة أسماء الميزات التي يمكن أن تدعم خط الأنابيب.

إعادة 2. ربما تكون محقًا في أنه من غير الود أنه ليس لدينا طريقة نظيفة لتطبيق ناقل النص على كل عمود. لست متأكدًا من كيفية تحقيق ذلك بشكل نظيف ، إلا إذا بدأنا ببساطة في دعم عدة أعمدة من المدخلات في CountVectorizer وما إلى ذلك.

شكرا لردك اللطيف!
كما أعلم ، عندما أعالج عمودًا مسبقًا باستخدام طرق يمكنها تغيير عمود واحد إلى أعمدة متعددة مثل OneHotEncoder ، CountVectorizer ، يمكنني الحصول على أسماء أعمدة البيانات الجديدة من محول الخطوة الأخيرة في خط الأنابيب عن طريق الوظيفة get_feature_names ، عند استخدام الطرق التي لا تنشئ أعمدة جديدة ، يمكنك فقط تعيين اسم الأعمدة الأولية.

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

باستخدام الرمز أعلاه ، يمكنني الحصول على أسماء أعمدة preprocesser الخاصة بي.
هل هذه الشفرة تحل هذا السؤال؟
اعتبارًا من eli5 ، لا أجد هذه الوظيفة ، هل يمكنك إعطائي رابطًا للمثال الصريح أو api لـ eli5؟

فيما يتعلق eli5 ، انظر transform_feature_names (المستخدمة من قبل شرح الأوزان)

1 هو نسخة مكررة من # 6425 ، أليس كذلك؟ أريد أن أكتب نومًا عن ذلك.
أعتقد أن دعم أعمدة نصية متعددة أمر سهل للغاية باستخدام ColumnTransformer . إنه ليس الرمز الأكثر جمالًا ولكن يمكنك فقط إضافة CountVectorizer لكل عمود نصي.

ولا يحل المقتطف الخاص بك المشكلة حقًا لأنه لا يوجد get_feature_names لا يعني أنه يمكنك فقط استخدام أسماء الأعمدة.

1 هو نسخة مكررة من # 6425 ، أليس كذلك؟ أريد أن أكتب نومًا عن ذلك.
أعتقد أن دعم أعمدة نصية متعددة أمر سهل للغاية باستخدام ColumnTransformer . إنه ليس الرمز الأكثر جمالًا ولكن يمكنك فقط إضافة CountVectorizer لكل عمود نصي.

ولا يحل المقتطف الخاص بك المشكلة حقًا لأنه لا يوجد get_feature_names لا يعني أنه يمكنك فقط استخدام أسماء الأعمدة.

نعم ، بعد تغذي Pandas DataFrame في خط أنابيب المعالجة المسبقة ، من الأفضل الحصول على أسماء الميزات حتى تتمكن من معرفة ما حدث بالضبط من البيانات التي تم إنشاؤها.

حسنًا ، سيتم الإغلاق كنسخة مكررة.

هذه ليست مشكلة حول ColumnTransformer.

  1. هو عن خط الأنابيب. لاحظ أن eli5 بتنفيذ وظيفة أسماء الميزات التي يمكن أن تدعم خط الأنابيب.

إعادة 2. ربما تكون محقًا في أنه من غير الود أنه ليس لدينا طريقة نظيفة لتطبيق ناقل النص على كل عمود. لست متأكدًا من كيفية تحقيق ذلك بشكل نظيف ، إلا إذا بدأنا ببساطة في دعم عدة أعمدة من المدخلات في CountVectorizer وما إلى ذلك.

شكرا لردك اللطيف!
كما أعلم ، عندما أعالج عمودًا مسبقًا باستخدام طرق يمكنها تغيير عمود واحد إلى أعمدة متعددة مثل OneHotEncoder ، CountVectorizer ، يمكنني الحصول على أسماء أعمدة البيانات الجديدة من محول الخطوة الأخيرة في خط الأنابيب عن طريق الوظيفة get_feature_names ، عند استخدام الطرق التي لا تنشئ أعمدة جديدة ، يمكنك فقط تعيين اسم الأعمدة الأولية.

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

باستخدام الرمز أعلاه ، يمكنني الحصول على أسماء أعمدة preprocesser الخاصة بي.
هل هذه الشفرة تحل هذا السؤال؟
اعتبارًا من eli5 ، لا أجد هذه الوظيفة ، هل يمكنك إعطائي رابطًا للمثال الصريح أو api لـ eli5؟

لقد أجريت تحسينًا بسيطًا لاستعادة الاسم مثل rawname_value لنماذج 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

هذه ليست مشكلة حول ColumnTransformer.

  1. هو عن خط الأنابيب. لاحظ أن eli5 بتنفيذ وظيفة أسماء الميزات التي يمكن أن تدعم خط الأنابيب.

إعادة 2. ربما تكون محقًا في أنه من غير الود أنه ليس لدينا طريقة نظيفة لتطبيق ناقل النص على كل عمود. لست متأكدًا من كيفية تحقيق ذلك بشكل نظيف ، إلا إذا بدأنا ببساطة في دعم عدة أعمدة من المدخلات في CountVectorizer وما إلى ذلك.

شكرا لردك اللطيف!
كما أعلم ، عندما أعالج عمودًا مسبقًا باستخدام طرق يمكنها تغيير عمود واحد إلى أعمدة متعددة مثل OneHotEncoder ، CountVectorizer ، يمكنني الحصول على أسماء أعمدة البيانات الجديدة من محول الخطوة الأخيرة في خط الأنابيب عن طريق الوظيفة get_feature_names ، عند استخدام الطرق التي لا تنشئ أعمدة جديدة ، يمكنك فقط تعيين اسم الأعمدة الأولية.

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

باستخدام الرمز أعلاه ، يمكنني الحصول على أسماء أعمدة preprocesser الخاصة بي.
هل هذه الشفرة تحل هذا السؤال؟
اعتبارًا من eli5 ، لا أجد هذه الوظيفة ، هل يمكنك إعطائي رابطًا للمثال الصريح أو api لـ eli5؟

ماذا لو قمت بتطبيق Simpleimputer مع add_indicator في خط الأنابيب؟ هذا النهج لن ينجح.

ماذا لو قمت بتطبيق Simpleimputer مع add_indicator في خط الأنابيب؟ هذا النهج لن ينجح.

سيكون من الجيد أن يكون لديك طريقة get_feature_names لهذا التكوين.

ماذا لو قمت بتطبيق Simpleimputer مع add_indicator في خط الأنابيب؟ هذا النهج لن ينجح.

ها هي مساهمتي في الحل قصير المدى. يقوم بإجبار جميع أنواع المصفوفات المختلفة على القوائم ، ويتعامل مع حالة SimpleImputer (add_indicate = True). إنه أيضًا مطول أكثر بقليل.

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

لمعلوماتك ، لقد كتبت بعض التعليمات البرمجية ومدونة حول كيفية استخراج أسماء الميزات من خطوط الأنابيب ومحولات الأعمدة المعقدة. الكود هو تحسين على رسالتي السابقة. https://towardsdatascience.com/extracting-plotting-feature-names-importance-from-scikit-learn-pipelines-eb5bfa6a31f4

@ kylegilde مقالة رائعة وشكراً على الكود. يعمل كالسحر. للحصول على تفسيرات عالمية ، كنت أتصارع مع handle_unkown='ignore'

إليك نسخة أخرى من مقتطف pjgao تتضمن أعمدة من التذكير:

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

ما رأيك في إضافة وظيفة مماثلة لقاعدة الكود الأساسية؟

هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات