Использование CountVectorizer с Pipeline и ColumnTransformer и получение AttributeError: объект 'numpy.ndarray' не имеет атрибута 'ниже'

Я пытаюсь использовать CountVectorizer() с Pipeline и ColumnTransformer. Поскольку CountVectorizer() создает разреженную матрицу, я использовал FunctionTransformer, чтобы убедиться, что ColumnTransformer может hstack правильно составить результирующую матрицу.

import pandas as pd
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import OneHotEncoder, FunctionTransformer
from sklearn.compose import ColumnTransformer
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.pipeline import Pipeline
from typing import Callable

# Dataset
df = pd.DataFrame([['a', 'Hi Tom', 'It is hot', 1],
                    ['b', 'How you been Tom', 'hot coffee', 2],
                    ['c', 'Hi you', 'I want some coffee', 3]],
                   columns=['col_for_ohe', 'col_for_countvectorizer_1', 'col_for_countvectorizer_2', 'num_col'])

# Use FunctionTransformer to ensure dense matrix
def tf_text(X, vectorizer_tf: Callable):
    X_vect_ = vectorizer_tf.fit_transform(X)
    return X_vect_.toarray()

tf_transformer = FunctionTransformer(tf_text, kw_args={'vectorizer_tf': CountVectorizer()})

# Transformation Pipelines
tf_transformer_pipe = Pipeline(
    steps = [('imputer', SimpleImputer(strategy='constant', fill_value='missing')),
             ('tf', tf_transformer)])

ohe_transformer_pipe = Pipeline(
    steps = [('imputer', SimpleImputer(strategy='constant', fill_value='missing')),
             ('ohe', OneHotEncoder(handle_unknown='ignore', sparse=False))])

transformer = ColumnTransformer(transformers=[
    ('cat_ohe', ohe_transformer_pipe, ['col_for_ohe']),
    ('cat_tf', tf_transformer_pipe, ['col_for_countvectorizer_1', 'col_for_countvectorizer_2'])
], remainder='passthrough')

transformed_df = transformer.fit_transform(df)

Я получаю AttributeError: объект 'numpy.ndarray' не имеет атрибута 'ниже'. Я видел этот вопрос и подозреваю, что CountVectorizer() является виновником, но не знаю, как его решить (в предыдущем вопросе ColumnTransformer не используется). Я наткнулся на DenseTransformer, который хотел бы использовать вместо FunctionTransformer, но, к сожалению, он не поддерживается в моей компании.

lower — это метод строк Python. панды также могут использовать его. Массив numpy не работает. Вам нужно выяснить, почему рассматриваемый объект является массивом. Вы должны изучить трассировку.
hpaulj 09.04.2022 08:40
Анализ настроения постов в Twitter с помощью Python, Tweepy и Flair
Анализ настроения постов в Twitter с помощью Python, Tweepy и Flair
Анализ настроения текстовых сообщений может быть настолько сложным или простым, насколько вы его сделаете. Как и в любом ML-проекте, вы можете выбрать...
7 лайфхаков для начинающих Python-программистов
7 лайфхаков для начинающих Python-программистов
В этой статье мы расскажем о хитростях и советах по Python, которые должны быть известны разработчику Python.
Установка Apache Cassandra на Mac OS
Установка Apache Cassandra на Mac OS
Это краткое руководство по установке Apache Cassandra.
Сертификатная программа "Кванты Python": Бэктестер ансамблевых методов на основе ООП
Сертификатная программа "Кванты Python": Бэктестер ансамблевых методов на основе ООП
В одном из недавних постов я рассказал о том, как я использую навыки количественных исследований, которые я совершенствую в рамках программы TPQ...
Создание персонального файлового хранилища
Создание персонального файлового хранилища
Вы когда-нибудь хотели поделиться с кем-то файлом, но он содержал конфиденциальную информацию? Многие думают, что электронная почта безопасна, но это...
Создание приборной панели для анализа данных на GCP - часть I
Создание приборной панели для анализа данных на GCP - часть I
Недавно я столкнулся с интересной бизнес-задачей - визуализацией сбоев в цепочке поставок лекарств, которую могут просматривать врачи и...
1
1
35
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Я думаю, вам действительно стоит еще раз оглянуться на свои основы. Ваш вопрос говорит мне о том, что вы недостаточно хорошо понимаете функцию, чтобы реализовать ее эффективно. Спросите еще раз, когда вы проведете достаточно исследований самостоятельно, чтобы не смущаться.

Да, я новичок. Но я стараюсь изо всех сил и надеюсь, что со временем мне станет лучше, если я получу помощь от тех, кто готов помочь. Я набрался смелости, чтобы опубликовать вопрос, несмотря на свое невежество, пожалуйста, не обескураживайте людей, которые пытаются стать лучше и еще только начинают свой путь.

DJL 09.04.2022 08:33

Ваш ответ может быть улучшен с помощью дополнительной вспомогательной информации. Пожалуйста, редактировать добавьте дополнительную информацию, например цитаты или документацию, чтобы другие могли подтвердить правильность вашего ответа. Дополнительную информацию о том, как писать хорошие ответы, можно найти в справочном центре.

Community 09.04.2022 10:42

В CountVectorizer передайте lower_case=False.

Я думаю, что подчеркивания нет, так как в нижнем регистре = False. Когда я пытаюсь это сделать, я получаю другую ошибку: «Ошибка типа: ожидаемая строка или объект, подобный байтам».

DJL 09.04.2022 08:42
Ответ принят как подходящий

Имо, первое, что нужно сделать, это то, что CountVectorizer() требует ввода 1D; ваш пример не работает, потому что вменение возвращает массив 2D numpy, что означает, что вам нужно добавить индивидуальную обработку, чтобы заставить его работать.

Затем вы также должны учитывать, что при использовании экземпляра CountVectorizer() (который, опять же, требует ввода 1D) в качестве преобразователя в ColumnTransformer(), вы должны передать колонны трансформаторов:

columns: str, array-like of str, int, array-like of int, array-like of bool, slice or callable

Indexes the data on its second axis. Integers are interpreted as positional columns, while strings can reference DataFrame columns by name. A scalar string or int should be used where transformer expects X to be a 1d array-like (vector), otherwise a 2d array will be passed to the transformer. [...]

Это было бы полезно при интерпретации фрагмента, который я опубликую как возможное решение.

import pandas as pd
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import OneHotEncoder, FunctionTransformer
from sklearn.compose import ColumnTransformer
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.pipeline import Pipeline
from typing import Callable
from sklearn.base import BaseEstimator, TransformerMixin

# Dataset
df = pd.DataFrame([['a', 'Hi Tom', 'It is hot', 1],
                ['b', 'How you been Tom', 'hot coffee', 2],
                ['c', 'Hi you', 'I want some coffee', 3]],
               columns=['col_for_ohe', 'col_for_countvectorizer_1', 'col_for_countvectorizer_2', 'num_col'])

class DimTransformer(BaseEstimator, TransformerMixin):
    def __init__(self):
        pass
    def fit(self, *_):
        return self
    def transform(self, X, *_):
        return pd.DataFrame(X)

# Use FunctionTransformer to ensure dense matrix
def tf_text(X, vectorizer_tf: Callable):
    X_vect_ = vectorizer_tf.fit_transform(X)
    return X_vect_.toarray()

tf_transformer = FunctionTransformer(tf_text, kw_args={'vectorizer_tf': CountVectorizer()})

# Transformation Pipelines
tf_transformer_pipe = Pipeline(
    steps = [('imputer', SimpleImputer(strategy='constant', fill_value='missing')), 
             ('dt', DimTransformer()),
             ('ct', ColumnTransformer([
                 ('tf1', tf_transformer, 0), 
                 ('tf2', tf_transformer, 1)
             ]))    
])

ohe_transformer_pipe = Pipeline(
    steps = [('imputer', SimpleImputer(strategy='constant', fill_value='missing')),
             ('ohe', OneHotEncoder(handle_unknown='ignore', sparse=False))])

transformer = ColumnTransformer(transformers=[
    ('cat_ohe', ohe_transformer_pipe, ['col_for_ohe']),
    ('cat_tf', tf_transformer_pipe, ['col_for_countvectorizer_1', 'col_for_countvectorizer_2'])
], remainder='passthrough')

transformed_df = transformer.fit_transform(df)

enter image description here

А именно, я добавляю преобразователь, который просто преобразует массив, возвращаемый экземпляром SimpleImputer, в DataFrame. Затем - и самое главное - поскольку кажется невозможным применить векторизацию к 2D-входу, полученному в результате двух предыдущих шагов ('imputer' и 'dt'), я добавляю еще один ColumnTransformer, который разделяет векторизацию на два параллельных шага (векторизация на столбец). Обратите внимание, что в этот момент ссылки на столбцы позиционируются, поскольку имена столбцов, возможно, изменились. Конечно, это индивидуальное решение, но, по крайней мере, может дать некоторые подсказки.

Учитывая, что на самом деле у вас нет пропущенных значений, вы можете увидеть, что это действительно работает, сравнив его с выводом:

dt = DimTransformer().fit_transform(df)
ct = ColumnTransformer([
    ('tf1', tf_transformer, 1), 
    ('tf2', tf_transformer, 2)
])
ct.fit_transform(dt)

print(ct.named_transformers_['tf1'].kw_args['vectorizer_tf'].vocabulary_) print(ct.named_transformers_['tf2'].kw_args['vectorizer_tf'].vocabulary_)

и заметив, что столбцы с четвертого до предпоследнего предыдущего вывода (а именно те, на которые повлияло применение 'cat_tf') совпадают с столбцами чуть ниже.

enter image description here

Вот пара постов, посвященных использованию CountVectorizer в экземпляре ColumnTransformer, хотя они не рассматривали предварительную подстановку набора данных.

Другие вопросы по теме