Pandas: Lista de deseos: hacer get_dummies () utilizable para el marco de entrenamiento / prueba

Creado en 28 nov. 2014  ·  21Comentarios  ·  Fuente: pandas-dev/pandas

Tener get_dummies () en Pandas es realmente bueno, pero para ser útil para el aprendizaje automático, debería ser utilizable en un marco de entrenamiento / prueba (o "fit_transform" y "transform", con la terminología sklearn). Avísame si esto necesita más explicaciones.

Entonces, supongo que este es un informe de error de la lista de deseos para agregar esa funcionalidad a Pandas. Incluso puedo crear una solicitud de extracción, si la gente está de acuerdo en que esto sería algo útil en Pandas (y están dispuestos a entrenar un poco y revisar el código para lo que sería mi primera contribución a este proyecto).

Categorical Reshaping Usage Question

Comentario más útil

Simplemente necesita hacer sus variables Categorical si desea especificar variables _posiblemente_ no observadas. Esto se puede hacer en el momento de la creación o después, consulte los documentos

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

La pregunta original no está bien especificada, por lo que se cierra.

Todos 21 comentarios

Bueno, ¿qué tal un ejemplo de pseudocódigo con entradas y salidas de un marco de muestra sería útil?

@ chrish42 , un ejemplo sería genial.

FYI scikit-learn tiene la clase OneHotEncoder que encaja en su canalización.

¿Algo como esto debería 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

Tenga cuidado con el orden de las columnas.

@TomAugspurger , en realidad, la compatibilidad con el proceso de procesamiento de sklearn no es la parte que me interesa. Lo que me gustaría es la capacidad de guardar la transformación realizada por get_dummes () en un conjunto de datos y luego aplicar dicha transformación tal cual (creando exactamente las mismas columnas), incluso si el segundo conjunto de datos tiene un subconjunto de los valores del primero en alguna columna, etc. Eso es realmente lo que quise decir con "utilizable en un marco de prueba / tren". ¿Es esta explicación más clara? (Puedo agregar un ejemplo que alguien crea que todavía es necesario).

Soy consciente de la clase OneHotEncoder en sklearn, pero tiene otras limitaciones.

Me encontré con el mismo problema que @ chrish42 y encontré get_dummies dándome un dolor de cabeza.

Ejemplo de las limitaciones del actual get dummies

Supongamos que trabajamos con datos del siguiente DataFrame df_train

`` `.python
df_train = pandas.DataFrame ({"coche": ["asiento", "bmw"], "color": ["rojo", "verde"]})
pandas.get_dummies (tren_df)

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 he observado un valor de "mercedes" para la variable "car" en df_train, me gustaría poder obtener la siguiente codificación en caliente:

`` `.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'}}

y esperaríamos que get_dummies regresara

`` `.python
get_dummies (prueba_df, valores_ aceptados por columna = valores_ aceptados por columna)

       car_bmw  car_seat  color_green  color_red

0 0 1 0 1
1 0 0 1 0
''

y espere que get_dummies (df_test) devuelva lo que ya devuelve.

Simplemente necesita hacer sus variables Categorical si desea especificar variables _posiblemente_ no observadas. Esto se puede hacer en el momento de la creación o después, consulte los documentos

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

La pregunta original no está bien especificada, por lo que se cierra.

Y cuando vaya en sentido contrario, desde la codificación hasta el Categorical, usará Categorical.from_codes.

Un consejo más no solicitado. Si le importan las estimaciones precisas de los coeficientes en los categóricos, elimine una de las columnas codificadas o, de lo contrario, tendrá multicolinealidad con la intersección (si tiene una).

El 5 de octubre de 2015, a las 05:34, Jeff Reback [email protected] escribió:

Simplemente necesita convertir sus variables en categóricas si desea especificar variables posiblemente no observadas. Esto se puede hacer en el momento de la creación o después, consulte los documentos

En [5]: df_train = pd.DataFrame ({"coche": Series (["asiento", "bmw"]). Astype ('categoría', categorías = ['asiento', 'bmw', 'mercedes'] ), "color": ["rojo", "verde"]})

En [6]: df_train
Fuera [6]:
color del coche
0 asiento rojo
1 bmw verde

En [7]: pd.get_dummies (df_train)
Fuera [7]:
car_seat car_bmw car_mercedes color_green color_red
0 1 0 0 0 1
1 0 1 0 1 0
La pregunta original no está bien especificada, por lo que se cierra.

-
Responda a este correo electrónico directamente o véalo en GitHub.

@TomAugspurger @jreback Creo que me he encontrado con el mismo problema últimamente y me gustaría dar un ejemplo

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

IsBadBuy Marca_BMW Marca_Mazda Marca_Toyota
0 0 0 0 1
1 1 0 1 0
2 0 1 0 0

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

Make_BMW Make_Toyota
0 0 1
1 1 0

Aquí, idealmente, la columna Make_Mazda debería conservarse, ya que el algoritmo ML esperaría el mismo número de características y los valores que obtenemos en la prueba serán un subconjunto de los del tren.

Utilice un categórico. Eso se expandirá al número correcto de columnas. Di una charla sobre esto si estás interesado https://m.youtube.com/watch?v=KLPtEBokqQ0

    _____________________________

De: Ajay Saxena [email protected]
Enviado: jueves 12 de enero de 2017 18:31
Asunto: Re: [pandas-dev / pandas] Lista de deseos: hacer get_dummies () utilizable para el marco de prueba / tren (# 8918)
Para: pandas-dev / pandas [email protected]
Cc: Tom Augspurger [email protected] , mencione menció[email protected]

@jreback Creo que me he encontrado con el mismo problema últimamente y me gustaría dar un ejemplo

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

IsBadBuy Marca_BMW Marca_Mazda Marca_Toyota
0 0 0 0 1
1 1 0 1 0
2 0 1 0 0

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

Make_BMW Make_Toyota
0 0 1
1 1 0

Aquí, idealmente, la columna Make_Mazda debería conservarse, ya que el algoritmo ML esperaría el mismo número de características y los valores que obtenemos en la prueba serán un subconjunto de los del tren.

-
Estás recibiendo esto porque te mencionaron.
Responda a este correo electrónico directamente, véalo en GitHub o silencia el hilo.

Gracias @TomAugspurger

La charla PyData Chicago 2016 impartida por @TomAugspurger estuvo muy bien hecha. Hizo un trabajo fantástico al ilustrar todas las razones por las que este problema / solicitud no debería cerrarse. En mi humilde opinión, su clase DummyEncoder o algún equivalente razonable deberían incluirse en Pandas propiamente dicho. Sí, puedo ir a su github y copiar / emular su clase, pero sería mucho mejor tenerlo compatible dentro de la biblioteca.

Creo que se necesita una biblioteca que se inicie en el modelado de datos
pipeline y funciona bien con pandas y scikit-learn.
Pero los pandas no dependen de scikit-learn y viceversa. Creo que hay
Espacio para otra biblioteca construida encima de ambos.

El miércoles 10 de mayo de 2017 a las 6:13 p.m., Brian Wylie [email protected]
escribió:

La charla PyData Chicago 2016 impartida por @TomAugspurger
https://github.com/TomAugspurger estuvo realmente bien hecho. Hizo un
fantástico trabajo de ilustrar todas las razones por las que este problema / solicitud debería
no estar cerrado. En mi humilde opinión, ya sea su clase DummyEncoder o algún razonable
el equivalente debe incluirse en Pandas propiamente dicho. Sí, puedo ir a su github
y copiar / emular su clase, pero sería mucho mejor tenerlo
soportado dentro de la biblioteca.

Por cierto, creo que @TomAugspurger https://github.com/TomAugspurger podría ser mi
nuevo gurú favorito de PyData. Voy a cazar todo lo que es
hecho / trabajando y tratar de absorberlo ... no de una manera espeluznante / acechante ... tú
sé de una manera normal que no es para nada espeluznante. :)

-
Estás recibiendo esto porque te mencionaron.
Responda a este correo electrónico directamente, véalo en GitHub
https://github.com/pandas-dev/pandas/issues/8918#issuecomment-300638388 ,
o silenciar el hilo
https://github.com/notifications/unsubscribe-auth/ABQHIpTqgHSE7iFVF9Pp4_YoKB9DPLcEks5r4kSrgaJpZM4DB6Hb
.

Aquí hay una pequeña solución en la que algunos de nosotros trabajamos que puede ser útil para algunos aquí. Variables ficticias con capacidades de ajuste / transformación.

https://github.com/joeddav/get_smarties

¡Los comentarios y las contribuciones serían útiles!

Esto parece estar relacionado con # 14017

He creado una solución que puede ser útil exactamente en este problema. Una variable categórica de Hot Encoding en un marco de pruebas de trenes. También puede manejar casos en los que el conjunto de datos es demasiado grande para caber en la memoria de la máquina.

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

También puede encontrar un pequeño tutorial sobre esto aquí .

@TomAugspurger Este código no funciona. Cuando voy a transformar mis datos de registro único de producción, solo me da la columna codificada en caliente para el valor único que está presente.
¿Qué me estoy perdiendo?

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

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

clase DummyEncoder (TransformerMixin):
def fit (self, X, y = Ninguno):
self.index_ = X.index
self.columns_ = X.columns
self.cat_columns_ = X.select_dtypes (include = ['categoría']). columnas
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 datos de SQL a pandas Dataframe

cnxn = pyodbc.connect ('DRIVER = {SQL Server}; SERVIDOR = {XXXXX}; BASE DE DATOS = {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] = 'En línea'
UNIÓN
SELECCIONAR 2 CONVERTIR (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] = 'Llamada entrante'
UNIÓN
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] = 'Llamada saliente'
"" "

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] = 'En línea'
"" "

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

imprimir (" * * * * Datos

* * * * * ")
imprimir (InputDataSet)

print (" * Información de columnas de categoría

* * ")
columnas = ['order_source_desc']
InputDataSet [columnas] = InputDataSet [columnas] .apply (lambda x: x.astype ('categoría'))

InputDataSet.info ()

print (" * Regresión lineal

* * ")

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

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

enc = DummyEncoder ()
enc.fit (X)

lluvia = enc.transform (X)

Prod = enc.transform (A)

imprimir (Prod)

SALIDA: * * * * Datos

* * * *
order_key service_date_key order_source_desc return_flag
0 10087937 20151214 En línea 1
1 10088174 20151201 Llamada entrante 2
2 10088553 20151217 Llamada entrante 2
3 663478 20160806 Llamada saliente 1
* Información de columnas de categoría * *

RangeIndex: 4 entradas, 0 a 3
Columnas de datos (4 columnas en total):
order_key 4 no nulo int64
service_date_key 4 int64 no nulo
order_source_desc 4 categoría no nula
return_flag 4 int64 no nulo
dtypes: categoría (1), int64 (3)
uso de memoria: 284,0 bytes
* Regresión lineal * * *
[[10087937 20151214 1]]

Así que creo que este hilo es un poco complicado, así que intentaré resumir una solución simple aquí y cómo esto ya es posible. Lo demostraré en una columna, pero puede generalizarlo a muchas.

Entonces, en la llamada "en forma", haz lo siguiente:

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

Almacena categories en el estado que está aprendiendo durante la adaptación.

Y luego en "transformar" haces:

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)

Y hará una codificación one-hot siempre en el mismo mapeo de valores. Si algún valor categórico no estuvo presente durante el entrenamiento, se verá como NaN durante la prueba. Si no se ve algún valor durante la prueba, no se establecerá ninguna columna para él.

Eso es muy lindo. Solo deseo que todos los que quieran hacer esto no tuvieran que descubrirlo de nuevo. ;-)

El enfoque sugerido por @mitar es un ejemplo breve y agradable. Para una exploración más detallada de este problema, aquí hay un cuaderno que podría ser útil / útil: https://nbviewer.jupyter.org/github/SuperCowPowers/scp-labs/blob/master/notebooks/Categorical_Encoding_Dangers.ipynb

Ví el siguiente código en el ejercicio del tutorial de Kaggle XGBoost. Esto hace el truco.

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)

También me he enfrentado al mismo problema varias veces. He escrito una clase (tomando ideas de esta discusión) a continuación que me facilitó las cosas.

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

También es fácil iniciar y usar una instancia del codificador.

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: Esto no se ocupará de ninguna excepción, por ejemplo, pasar los nombres de las columnas que no están disponibles en el marco de datos.

¿Fue útil esta página
0 / 5 - 0 calificaciones
bleepcoder.com utiliza la información de GitHub con licencia pública para proporcionar a los desarrolladores de todo el mundo soluciones a sus problemas. No estamos afiliados a GitHub, Inc. o a ningún desarrollador que use GitHub para sus proyectos. No alojamos ninguno de los vídeos o imágenes en nuestros servidores. Todos los derechos pertenecen a sus respectivos propietarios.
Fuente de esta página: Fuente

Lenguajes de programación populares
Proyectos populares de GitHub
Más proyectos de 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.