Pandas: Lista de desejos: tornar get_dummies () utilizável para estrutura de treinamento / teste

Criado em 28 nov. 2014  ·  21Comentários  ·  Fonte: pandas-dev/pandas

Ter get_dummies () no Pandas é muito bom, mas para ser útil no aprendizado de máquina, ele precisaria ser utilizável em uma estrutura de treinamento / teste (ou "fit_transform" e "transform", com a terminologia sklearn). Avise-me se precisar de mais explicações.

Então, eu acho que este é um relatório de bug da lista de desejos para adicionar essa funcionalidade ao Pandas. Posso até criar uma solicitação de pull, se as pessoas concordarem que isso seria algo útil para ter no Pandas (e estão dispostas a treinar um pouco e fazer revisão de código para o que seria minha primeira contribuição para este projeto).

Categorical Reshaping Usage Question

Comentários muito úteis

Você simplesmente precisa tornar suas variáveis Categorical se quiser especificar _possivelmente_ variáveis ​​não observadas. Isso pode ser feito no momento da criação ou depois, consulte a documentação

In [5]: df_train = pd.DataFrame({"car":Series(["seat","bmw"]).astype('category',categories=['seat','bmw','mercedes']),"color":["red","green"]})

In [6]: df_train
Out[6]: 
    car  color
0  seat    red
1   bmw  green

In [7]: pd.get_dummies(df_train )
Out[7]: 
   car_seat  car_bmw  car_mercedes  color_green  color_red
0         1        0             0            0          1
1         0        1             0            1          0

A pergunta original não está bem especificada, portanto, encerrando.

Todos 21 comentários

bem, que tal um exemplo de pseudo código com entradas e saídas de um quadro de amostra seria útil

@ chrish42 , um exemplo seria ótimo.

Para sua informação, o scikit -learn tem a classe

Algo assim deve funcionar?

import pandas as pd
from sklearn.pipeline import TransformerMixin

class DummyEncoder(TransformerMixin):

    def __init__(self, columns=None):

        self.columns = columns

    def transform(self, X, y=None, **kwargs):

        return pd.get_dummies(X, columns=self.columns)

    def fit(self, X, y=None, **kwargs):

        return self

Dando

In [15]: df
Out[15]: 
   A  B  C
0  1  a  a
1  2  b  a

In [16]: DummyEncoder().transform(df)
Out[16]: 
   A  B_a  B_b  C_a
0  1    1    0    1
1  2    0    1    1

Tenha cuidado com a ordem das colunas.

@TomAugspurger , na verdade, a compatibilidade com o pipeline de processamento do sklearn em si não é a parte que me interessa. O que eu gostaria é a capacidade de salvar a transformação feita por get_dummes () em um conjunto de dados e, em seguida, aplicar essa transformação como está (criando exatamente as mesmas colunas), mesmo se o segundo conjunto de dados tiver um subconjunto dos valores do primeiro em alguma coluna, etc. Isso é realmente o que eu quis dizer com "utilizável em uma estrutura de treinamento / teste". Esta explicação é mais clara? (Posso adicionar um exemplo que alguém pensa que ainda é necessário.)

Estou ciente da classe OneHotEncoder no sklearn, mas ela tem outras limitações.

Eu tropecei no mesmo problema de @ chrish42 e descobri que get_dummies está me dando dor de cabeça.

Exemplo das limitações do atual get dummies

Vamos supor que trabalhamos com dados do seguinte df_train DataFrame

`` `.python
df_train = pandas.DataFrame ({"carro": ["assento", "bmw"], "cor": ["vermelho", "verde"]})
pandas.get_dummies (df_train)

car_bmw car_seat color_green color_red
0 0 1 0 1
1 1 0 1 0


Then we are provided with

``` .python
df_test = pandas.DataFrame({"car":["seat","mercedes"], "color":["red","green"]})
pandas.get_dummies(df_test )

         car_mercedes  car_seat  color_green  color_red
0             0         1            0          1
1             1         0            1          0

Como nunca observei um valor "mercedes" para a variável "carro" em df_train, gostaria de obter a seguinte codificação quente:

`` `.python
car_bmw car_seat color_green color_red
0 0 1 0 1
1 0 0 1 0


Where the column car_mercedes actually never appears.

This could be solved by allowing get_dummies to receive an input dictionary stating the accepted values that we allow for each column.  

Returning to the previous example, we could give as input to get_dummies the following dict of sets

``` .python
accepted_values_per_column = {'car': {'bmw', 'seat'}, 'color': {'green', 'red'}}

e esperamos que get_dummies retorne

`` `.python
get_dummies (df_test, accept_values_per_column = allowed_values_per_column)

       car_bmw  car_seat  color_green  color_red

0 0 1 0 1
1 0 0 1 0
`` `

e espere get_dummies (df_test) para retornar o que já retornou.

Você simplesmente precisa tornar suas variáveis Categorical se quiser especificar _possivelmente_ variáveis ​​não observadas. Isso pode ser feito no momento da criação ou depois, consulte a documentação

In [5]: df_train = pd.DataFrame({"car":Series(["seat","bmw"]).astype('category',categories=['seat','bmw','mercedes']),"color":["red","green"]})

In [6]: df_train
Out[6]: 
    car  color
0  seat    red
1   bmw  green

In [7]: pd.get_dummies(df_train )
Out[7]: 
   car_seat  car_bmw  car_mercedes  color_green  color_red
0         1        0             0            0          1
1         0        1             0            1          0

A pergunta original não está bem especificada, portanto, encerrando.

E quando você está indo para o outro lado, da codificação de volta para Categórico, você usará Categorical.from_codes.

Mais um conselho não solicitado. Se você se preocupa com estimativas precisas dos coeficientes nas categóricas, elimine uma das colunas codificadas ou você terá multicolinearidade com a interceptação (se houver).

Em 5 de outubro de 2015, às 05:34, Jeff Reback [email protected] escreveu:

Você simplesmente precisa tornar suas variáveis ​​categóricas se quiser especificar variáveis ​​possivelmente não observadas. Isso pode ser feito no momento da criação ou depois, consulte a documentação

Em [5]: df_train = pd.DataFrame ({"carro": Series (["assento", "bmw"]). Astype ('categoria', categorias = ['assento', 'bmw', 'mercedes'] ), "color": ["red", "green"]})

Em [6]: df_train
Fora [6]:
cor do carro
0 assento vermelho
1 bmw verde

Em [7]: pd.get_dummies (df_train)
Fora [7]:
car_seat car_bmw car_mercedes color_green color_red
0 1 0 0 0 1
1 0 1 0 1 0
A pergunta original não está bem especificada, portanto, encerrando.

-
Responda a este e-mail diretamente ou visualize-o no GitHub.

@TomAugspurger @jreback Acho que tive o mesmo problema recentemente e gostaria de citar um exemplo

train_a = pd.DataFrame ({"IsBadBuy": [0,1,0], "Make": ['Toyota', 'Mazda', 'BMW']})

IsBadBuy Make_BMW Make_Mazda Make_Toyota
0 0 0 0 1
1 1 0 1 0
2 0 1 0 0

test_a = pd.DataFrame ({"Make": ['Toyota', 'BMW']})
imprimir pd.get_dummies (test_a, colunas = ['Make'])

Make_BMW Make_Toyota
0 0 1
1 1 0

Aqui, idealmente, a coluna Make_Mazda deve ser preservada, visto que o algoritmo de ML espera o mesmo número de recursos e os valores que obtemos no teste serão um subconjunto dos que estão em andamento.

Use um categórico. Isso se expandirá para o número correto de colunas. Eu dei uma palestra sobre isso se você estiver interessado https://m.youtube.com/watch?v=KLPtEBokqQ0

    _____________________________

De: Ajay Saxena [email protected]
Enviado: quinta-feira, 12 de janeiro de 2017 18:31
Assunto: Re: [pandas-dev / pandas] Lista de desejos: tornar get_dummies () utilizável para a estrutura de treinamento / teste (# 8918)
Para: pandas-dev / pandas [email protected]
Cc: Tom Augspurger [email protected] , Menção mençã[email protected]

@jreback , acho que tive o mesmo problema recentemente e gostaria de dar um exemplo

train_a = pd.DataFrame ({"IsBadBuy": [0,1,0], "Make": ['Toyota', 'Mazda', 'BMW']})

IsBadBuy Make_BMW Make_Mazda Make_Toyota
0 0 0 0 1
1 1 0 1 0
2 0 1 0 0

test_a = pd.DataFrame ({"Make": ['Toyota', 'BMW']})
imprimir pd.get_dummies (test_a, colunas = ['Make'])

Make_BMW Make_Toyota
0 0 1
1 1 0

Aqui, idealmente, a coluna Make_Mazda deve ser preservada, visto que o algoritmo de ML espera o mesmo número de recursos e os valores que obtemos no teste serão um subconjunto dos que estão em andamento.

-
Você está recebendo isso porque foi mencionado.
Responda a este e-mail diretamente, visualize-o no GitHub ou ignore a conversa.

Obrigado @TomAugspurger

A palestra do PyData Chicago 2016 dada por @TomAugspurger foi muito bem feita. Ele fez um trabalho fantástico ao ilustrar todos os motivos pelos quais esse problema / solicitação não deveria ser encerrado. IMHO ou sua classe DummyEncoder ou algum equivalente razoável deve ser incluído no Pandas apropriado. Sim, eu posso ir ao github e copiar / emular sua classe, mas seria muito mais agradável apenas tê-la suportada na biblioteca.

Eu acho que há uma necessidade de uma biblioteca que vá no início da modelagem de dados
pipeline e funciona bem com pandas e scikit-learn.
Mas os pandas não dependem do scikit-learn e vice-versa. Acho que tem
espaço para outra biblioteca construída em cima de ambos.

Na quarta-feira, 10 de maio de 2017 às 18:13, Brian Wylie [email protected]
escrevi:

A palestra PyData Chicago 2016 dada por @TomAugspurger
https://github.com/TomAugspurger foi muito bem feito. Ele fez um
trabalho fantástico de ilustrar todas as razões pelas quais este problema / solicitação deve
não ser fechado. IMHO quer sua classe DummyEncoder ou algum razoável
equivalente deve ser incluído no Pandas apropriado. Sim, posso ir ao github dele
e copiar / emular sua classe, mas seria muito melhor apenas tê-la
compatível com a biblioteca.

BTW, acho que @TomAugspurger https://github.com/TomAugspurger pode ser meu
novo guru favorito de PyData. Vou caçar tudo que ele
feito / trabalhando e tentando absorvê-lo .. não de uma forma assustadora / perseguidora .. você
sei apenas de uma maneira normal que não é nem um pouco assustador. :)

-
Você está recebendo isso porque foi mencionado.
Responda a este e-mail diretamente, visualize-o no GitHub
https://github.com/pandas-dev/pandas/issues/8918#issuecomment-300638388 ,
ou silenciar o tópico
https://github.com/notifications/unsubscribe-auth/ABQHIpTqgHSE7iFVF9Pp4_YoKB9DPLcEks5r4kSrgaJpZM4DB6Hb
.

Aqui está uma pequena solução na qual alguns de nós trabalharam que pode ser útil para alguns aqui. Variáveis ​​fictícias com recursos de ajuste / transformação.

https://github.com/joeddav/get_smarties

Feedback e contribuições seriam úteis!

Isso parece relacionado a # 14017

Eu criei uma solução que pode ser útil exatamente neste problema. Uma variável categórica Hot Encoding em uma estrutura de teste de trem. Ele também pode lidar com casos em que o conjunto de dados é muito grande para caber na memória da máquina.

https://github.com/yashu-seth/dummyPy

Você também pode encontrar um pequeno tutorial sobre isso aqui .

@TomAugspurger Este código não funciona. Quando vou transformar meus dados de registro único de produção, isso só me dá uma coluna codificada a quente para o valor único que está presente.
o que estou perdendo?

importar pyodbc
importar picles
de sklearn.linear_model import LogisticRegression
de sklearn.linear_model import LinearRegression

importar numpy como np
importar pandas como pd
de sklearn.pipeline import TransformerMixin
de sklearn.pipeline import make_pipeline

classe DummyEncoder (TransformerMixin):
def fit (self, X, y = None):
self.index_ = X.index
self.columns_ = X.columns
self.cat_columns_ = X.select_dtypes (include = ['categoria']). colunas
self.non_cat_columns_ = X.columns.drop (self.cat_columns_)

    self.cat_map_ = {col: X[col].cat for col in self.cat_columns_}

    left = len(self.non_cat_columns_)
    self.cat_blocks_ = {}
    for col in self.cat_columns_:
        right = left + len(X[col].cat.categories)
        self.cat_blocks_[col], left = slice(left, right), right
    return self

def transform(self, X, y=None):
    return np.asarray(pd.get_dummies(X))

def inverse_transform(self, X):
    non_cat = pd.DataFrame(X[:, :len(self.non_Cat_columns_)],
                             columns=self.non_cat_columns_)
    cats = []
    for col, cat in self.cat_map_.items():
        slice_ = self.cat_blocks_[col]
        codes = X[:, slice_].argmax(1)
        series = pd.Series(pd.Categorical.from_codes(
                codes, cat.categories, ordered=cat.ordered
        ), name=col)
        cats.append(series)
    df = pd.concat([non_cat] + cats, axis=1)[self.columns_]
    return df

importar dados de SQL para pandas Dataframe

cnxn = pyodbc.connect ('DRIVER = {SQL Server}; SERVER = {XXXXX}; DATABASE = {ML_Learn_Taxi}; UID = {XXXX}; PWD = {XXXX}')
sql = "" "
SELECT top 1 CONVERT (int, [order_key]) order_key
, CONVERT (int, [service_date_key]) service_date_key
, [order_source_desc]
, 1 como 'return_flag'
FROM [ML_Return_Customer]. [Dbo]. [Return_customers_test_set]
WHERE [order_source_desc] = 'Online'
UNIÃO
SELECT top 2 CONVERT (int, [order_key])
, CONVERT (int, [service_date_key])
, [order_source_desc]
2
FROM [ML_Return_Customer]. [Dbo]. [Return_customers_test_set]
WHERE [order_source_desc] = 'Chamada de entrada'
UNIÃO
SELECT top 1 CONVERT (int, [order_key])
, CONVERT (int, [service_date_key])
, [order_source_desc]
, 1
FROM [ML_Return_Customer]. [Dbo]. [Return_customers_test_set]
WHERE [order_source_desc] = 'Chamada de saída'
"" "

prod_sql = "" "
SELECT top 1 CONVERT (int, [order_key]) order_key
, CONVERT (int, [service_date_key]) service_date_key
, [order_source_desc]
, 1 como 'return_flag'
FROM [ML_Return_Customer]. [Dbo]. [Return_customers_test_set]
WHERE [order_source_desc] = 'Online'
"" "

InputDataSet = pd.read_sql (sql, cnxn)
ProdDataSet = pd.read_sql (prod_sql, cnxn)

imprimir (" * * * * Dados

* * * * * ")
imprimir (InputDataSet)

imprimir (" * Informações das colunas da categoria

* * ")
colunas = ['order_source_desc']
InputDataSet [colunas] = InputDataSet [colunas] .apply (lambda x: x.astype ('categoria'))

InputDataSet.info ()

print (" * Regressão Linear

* * ")

X = InputDataSet.drop ('return_flag', axis = 1)
y = InputDataSet ['return_flag']

A = ProdDataSet.drop ('return_flag', axis = 1)
B = ProdDataSet ['return_flag']

enc = DummyEncoder ()
enc.fit (X)

rain = enc.transform (X)

Prod = enc.transform (A)

imprimir (Prod)

SAÍDA: * * * * Dados

* * * *
order_key service_date_key order_source_desc return_flag
0 10087937 20151214 online 1
1 10088174 20151201 Chamada de entrada 2
2 10088553 20151217 Chamada de entrada 2
3 663478 20160806 Chamada de saída 1
* Informações das colunas da categoria * *

RangeIndex: 4 entradas, 0 a 3
Colunas de dados (total de 4 colunas):
order_key 4 não nulo int64
service_date_key 4 não nulo int64
order_source_desc 4 categoria não nula
return_flag 4 não nulo int64
dtipos: categoria (1), int64 (3)
uso de memória: 284,0 bytes
* Regressão Linear * * *
[[10087937 20151214 1]]

Acho que esse tópico está um pouco confuso, então tentarei resumir uma solução simples aqui e como isso já é possível. Vou demonstrar em uma coluna, mas você pode generalizar para muitas.

Então, na chamada "fit", você apenas faz:

categories = sorted(training_data.iloc[:, column_index].value_counts(dropna=True).index)

Você armazena categories no estado que está aprendendo durante a adaptação.

E então, em "transformar" você:

from pandas.api import types as pandas_types

categorical_data = testing_data.iloc[:, [column_index]].astype(
    pandas_types.CategoricalDtype(categories=categories),
)
one_hot_encoded = pandas.get_dummies(categorical_data)

E fará a codificação one-hot sempre no mesmo mapeamento para valores. Se algum valor categórico não estiver presente durante o treinamento, ele será visto como NaN durante o teste. Se algum valor não for visto durante o teste, nenhuma coluna será definida para ele.

Isso é muito simpático. Só desejo que todos que desejam fazer isso não precisem descobrir de novo. ;-)

A abordagem sugerida por @mitar é um bom e breve exemplo. Para uma exploração mais aprofundada deste problema, aqui está um bloco de notas que pode ser útil / útil: https://nbviewer.jupyter.org/github/SuperCowPowers/scp-labs/blob/master/notebooks/Categorical_Encoding_Dangers.ipynb

Vi o código abaixo no exercício do tutorial do Kaggle XGBoost. Isso resolve o problema.

X_train = pd.get_dummies(X_train)
X_valid = pd.get_dummies(X_valid)
X_test = pd.get_dummies(X_test)
X_train, X_valid = X_train.align(X_valid, join='left', axis=1)
X_train, X_test = X_train.align(X_test, join='left', axis=1)

Também enfrentei o mesmo problema várias vezes. Eu escrevi uma aula (tirando ideias desta discussão) abaixo que tornou as coisas mais fáceis para mim.

import pandas
from sklearn.preprocessing import LabelEncoder

class CategoryEncoder:
    '''
    labelEncoding : boolean -> True If the categorical columns are to be label encoded
    oneHotEncoding : boolean -> True If the categorical columns are to be one hot encoded (using pandas.get_dummies method)
    dropFirst : boolean -> True if first column is to be dropped (usually to avoid multi-collinearity) post one hot encoding
                           Doesn't matter if oneHotEncoding = False

    df : pandas.DataFrame() -> dataframe object that needs to be encoded
    catCols : list -> list of the categorical columns that need to be encoded
    '''
    def __init__(self,labelEncoding=True,oneHotEncoding=False,dropFirst=False):
        self.labelEncoding = labelEncoding
        self.oneHotEncoding = oneHotEncoding
        self.dropFirst = dropFirst
        self.labelEncoder = {}
        self.oneHotEncoder = {}

    def fit(self,df,catCols=[]):
        df1 = df.copy()
        if self.labelEncoding:
            for col in catCols:
                labelEncoder = LabelEncoder()
                labelEncoder.fit(df1.loc[:,col].astype(str))
                df1.loc[:,col] = labelEncoder.transform(df1.loc[:,col])
                self.labelEncoder[col] = labelEncoder.classes_

        if self.oneHotEncoding:
            for col in catCols:
                cats = sorted(df1.loc[:,col].value_counts(dropna=True).index)
                self.oneHotEncoder[col] = cats

    def transform(self,df,catCols=[]):
        df1 = df.copy()
        if self.labelEncoding:
            for col in catCols:
                labelEncoder = self.labelEncoder[col]
                labelEncoder = {v:i for i,v in enumerate(labelEncoder.tolist())}
                print(labelEncoder)
                df1.loc[:,col] = df1.loc[:,col].map(labelEncoder)

        if self.oneHotEncoding:
            for col in catCols:
                oneHotEncoder = self.oneHotEncoder[col]
                df1.loc[:,col] = df1.loc[:,col].astype(pandas.CategoricalDtype(categories=oneHotEncoder))
            df1 = pandas.get_dummies(df1,columns=catCols,drop_first=self.dropFirst)

        return df1

Fácil de iniciar e usar uma instância do codificador também.

enc1 = CategoryEncoder(True,False)     # Will label encode but not one-hot encode
enc2 = CategoryEncoder(False,True,True)     # Will one-hot encode but not label encode
enc3 = CategoryEncoder(True,True,True)     # Will label encode first and then one-hot encode

# List of categorical columns you want to encode
categorical_columns = ['col_1', 'col_2']

enc1.fit(train_df, categorical_columns)
enc1.transform(test_df, categorical_columns) # Returns the dataframe encoded columns

NOTA: Isso não cuidará de nenhuma exceção, por exemplo, passar os nomes das colunas que não estão disponíveis no dataframe

Esta página foi útil?
0 / 5 - 0 avaliações

Questões relacionadas

nathanielatom picture nathanielatom  ·  3Comentários

idanivanov picture idanivanov  ·  3Comentários

venuktan picture venuktan  ·  3Comentários

ericdf picture ericdf  ·  3Comentários

Ashutosh-Srivastav picture Ashutosh-Srivastav  ·  3Comentários
bleepcoder.com usa informações licenciadas publicamente pela GitHub para fornecer aos desenvolvedores em todo o mundo soluções para seus problemas. Não somos afiliados à GitHub, Inc. nem a nenhum desenvolvedor que utilize GitHub para seus projetos. Nós não hospedamos nenhum dos vídeos ou imagens em nossos servidores. Todos os direitos pertencem a seus respectivos proprietários.
Fonte para esta página: Fonte

Linguagens de programação populares
Projetos populares do GitHub
Mais projetos GitHub

© 2024 bleepcoder.com - Contact
Made with in the Dominican Republic.
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.