Quando eu uso o ColumnTransformer para pré-processar colunas diferentes (incluindo numérico, categoria, texto) com pipeline, não consigo obter os nomes dos recursos dos dados transformados finais, o que é difícil de depurar.
Aqui está o código:
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)
preprocesser.get_feature_names()
obterá o erro:AttributeError: Transformer numeric (type Pipeline) does not provide get_feature_names.
ColumnTransformer
, text_transformer
só pode processar uma string (por exemplo, 'Sexo'), mas não uma lista de strings como text_columns
Este não é um problema sobre o ColumnTransformer.
eli5
implementa uma função de nomes de recursos que pode oferecer suporte a Pipeline.Re 2. talvez você esteja certo ao dizer que não é nada amigável não termos uma maneira limpa de aplicar um vetorizador de texto a cada coluna. Não tenho certeza de como isso pode ser alcançado de forma limpa, a menos que simplesmente comecemos a suportar várias colunas de entrada no CountVectorizer etc.
Este não é um problema sobre o ColumnTransformer.
- é sobre Pipeline. Observe que
eli5
implementa uma função de nomes de recursos que pode oferecer suporte a Pipeline.Re 2. talvez você esteja certo ao dizer que não é nada amigável não termos uma maneira limpa de aplicar um vetorizador de texto a cada coluna. Não tenho certeza de como isso pode ser alcançado de forma limpa, a menos que simplesmente comecemos a suportar várias colunas de entrada no CountVectorizer etc.
Obrigado pela sua amável resposta!
Como eu sei, quando pré-processo uma coluna usando métodos que podem alterar uma coluna para várias colunas, como OneHotEncoder
, CountVectorizer
, posso obter os novos nomes das colunas de dados do transformador da última etapa do pipeline função get_feature_names
, ao usar métodos que não criam novas colunas, pode apenas definir o nome das colunas brutas.
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
Usando o código acima, posso obter os nomes das minhas colunas de preprocesser
.
Esses códigos resolvem essa questão?
A partir de eli5, não encontro essa função. Você pode me dar um link para o exemplo explícito ou api para eli5?
Com relação a eli5, consulte transform_feature_names (usado por explain_weights)
1 é uma duplicata de # 6425, certo? Eu quero escrever um slep sobre isso.
Acho que suportar várias colunas de texto é muito fácil com ColumnTransformer
. Não é o código mais bonito, mas você pode simplesmente adicionar um CountVectorizer para cada coluna de texto.
E seu snippet não resolve realmente o problema porque nenhum get_feature_names
não significa que você pode apenas usar os nomes das colunas.
1 é uma duplicata de # 6425, certo? Eu quero escrever um slep sobre isso.
Acho que suportar várias colunas de texto é muito fácil comColumnTransformer
. Não é o código mais bonito, mas você pode simplesmente adicionar um CountVectorizer para cada coluna de texto.E seu snippet não resolve realmente o problema porque nenhum
get_feature_names
não significa que você pode apenas usar os nomes das colunas.
sim, depois que um DataFrame do pandas for alimentado em um pipeline de pré-processo, é melhor obter nomes de recursos para que possamos saber exatamente o que aconteceu apenas a partir dos dados gerados.
ok, fechando como duplicata.
Este não é um problema sobre o ColumnTransformer.
- é sobre Pipeline. Observe que
eli5
implementa uma função de nomes de recursos que pode oferecer suporte a Pipeline.Re 2. talvez você esteja certo ao dizer que não é nada amigável não termos uma maneira limpa de aplicar um vetorizador de texto a cada coluna. Não tenho certeza de como isso pode ser alcançado de forma limpa, a menos que simplesmente comecemos a suportar várias colunas de entrada no CountVectorizer etc.
Obrigado pela sua amável resposta!
Como eu sei, quando pré-processo uma coluna usando métodos que podem alterar uma coluna para várias colunas, comoOneHotEncoder
,CountVectorizer
, posso obter os novos nomes das colunas de dados do transformador da última etapa do pipeline funçãoget_feature_names
, ao usar métodos que não criam novas colunas, pode apenas definir o nome das colunas brutas.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
Usando o código acima, posso obter os nomes das minhas colunas de
preprocesser
.
Esses códigos resolvem essa questão?
A partir de eli5, não encontro essa função. Você pode me dar um link para o exemplo explícito ou api para eli5?
Fiz um pequeno aprimoramento para recuperar o nome como rawname_value para formulários 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
Este não é um problema sobre o ColumnTransformer.
- é sobre Pipeline. Observe que
eli5
implementa uma função de nomes de recursos que pode oferecer suporte a Pipeline.Re 2. talvez você esteja certo ao dizer que não é nada amigável não termos uma maneira limpa de aplicar um vetorizador de texto a cada coluna. Não tenho certeza de como isso pode ser alcançado de forma limpa, a menos que simplesmente comecemos a suportar várias colunas de entrada no CountVectorizer etc.
Obrigado pela sua amável resposta!
Como eu sei, quando pré-processo uma coluna usando métodos que podem alterar uma coluna para várias colunas, comoOneHotEncoder
,CountVectorizer
, posso obter os novos nomes das colunas de dados do transformador da última etapa do pipeline funçãoget_feature_names
, ao usar métodos que não criam novas colunas, pode apenas definir o nome das colunas brutas.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
Usando o código acima, posso obter os nomes das minhas colunas de
preprocesser
.
Esses códigos resolvem essa questão?
A partir de eli5, não encontro essa função. Você pode me dar um link para o exemplo explícito ou api para eli5?
E se você aplicar simpleimputer com add_indicator em um pipeline? Essa abordagem não funcionará.
E se você aplicar simpleimputer com add_indicator em um pipeline? Essa abordagem não funcionará.
Seria bom ter um método get_feature_names para esta configuração.
E se você aplicar simpleimputer com add_indicator em um pipeline? Essa abordagem não funcionará.
Aqui está minha contribuição para a solução de curto prazo. Ele força todos os diferentes tipos de array a listas e trata o caso de SimpleImputer (add_indicate = True). Também é um pouco mais prolixo.
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
Para sua informação, escrevi um código e um blog sobre como extrair os nomes dos recursos de Pipelines e Transformadores de Coluna complexos. O código é uma melhoria em relação ao meu post anterior. https://towardsdatascience.com/extracting-plotting-feature-names-importance-from-scikit-learn-pipelines-eb5bfa6a31f4
@kylegilde Excelente artigo e obrigado pelo código. Funciona como um encanto. Para explicações globais, eu estava lutando com KernelSHAP e álibi por algumas horas, mas não consegui fazer meu transformador onehot funcionar sem handle_unkown='ignore'
Aqui está outra versão do snippet de @pjgao que inclui colunas do lembrete:
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
O que você acha de adicionar uma função semelhante à base de código principal?
Comentários muito úteis
Obrigado pela sua amável resposta!
Como eu sei, quando pré-processo uma coluna usando métodos que podem alterar uma coluna para várias colunas, como
OneHotEncoder
,CountVectorizer
, posso obter os novos nomes das colunas de dados do transformador da última etapa do pipeline funçãoget_feature_names
, ao usar métodos que não criam novas colunas, pode apenas definir o nome das colunas brutas.Usando o código acima, posso obter os nomes das minhas colunas de
preprocesser
.Esses códigos resolvem essa questão?
A partir de eli5, não encontro essa função. Você pode me dar um link para o exemplo explícito ou api para eli5?