Scikit-learn: ¿Pandas adentro, pandas afuera?

Creado en 22 oct. 2015  ·  59Comentarios  ·  Fuente: scikit-learn/scikit-learn

Por el momento, es posible usar un marco de datos de pandas como entrada para la mayoría de los métodos de ajuste / predicción / transformación de sklearn, pero obtienes una matriz numerosa. Sería muy bueno poder obtener datos en el mismo formato en el que los colocó.

Esto no es del todo sencillo, porque si su marco de datos contiene columnas que no son numéricas, entonces las matrices intermedias numpy harán que sklearn falle, porque serán dtype=object , en lugar de dtype=float . Esto se puede resolver con un transformador Dataframe-> ndarray, que mapea los datos no numéricos a los datos numéricos (por ejemplo, números enteros que representan clases / categorías). sklearn-pandas ya hace esto, aunque actualmente no tiene un inverse_transform , pero eso no debería ser difícil de agregar.

Siento que una transformación como esta sería _realmente_ útil para tener en sklearn; es el tipo de cosas que cualquier persona que trabaje con conjuntos de datos con múltiples tipos de datos podría encontrar útil. ¿Qué se necesitaría para incluir algo como esto en sklearn?

Comentario más útil

Todos mis transformadores devuelven DataFrame s cuando se les da DataFrame s.
Cuando ingreso un DataFrame 300 columnas en un Pipeline y recibo un ndarray 500 columnas, no puedo aprender mucho de él de manera efectiva, por ejemplo, feature_selection , porque ya no tengo los nombres de las columnas. Si, digamos, mutual_info_classif me dice que solo las columnas 30 y 75 son importantes, no puedo averiguar cómo simplificar mi Pipeline para la producción.
Por lo tanto, es fundamental para mi caso de uso mantener mis datos en un DataFrame .
Gracias.

Todos 59 comentarios

Scikit-learn fue diseñado para trabajar con un formato de entrada muy genérico. Quizás el mundo alrededor de scikit-learn ha cambiado mucho desde entonces de manera que hace que la integración de Pandas sea más importante. Todavía podría ser suministrado en gran parte por envoltorios de terceros.

Pero aparte de la pregunta más amplia, creo que debería intentar dar ejemplos de cómo la salida compatible con Pandas de los estimadores estándar diferirá y marcará la diferencia en la usabilidad. Ejemplos en los que puedo pensar:

  • todos los métodos pueden copiar el índice de la entrada
  • los transformadores deben generar columnas con el nombre apropiado
  • multiclass predict_proba puede etiquetar columnas con nombres de clases

Sí, fuera de mi cabeza:

  • el índice puede ser realmente útil, por ejemplo, para crear variables retrasadas cronometradas (por ejemplo, retraso de 1 día, en datos diarios con algunos días faltantes)
  • Los regresores sklearn se pueden usar de forma transparente con datos categóricos (pasar un marco de datos mixto, transformar columnas categóricas con LabelBinarizer, el inverso_transformarlo).
  • sklearn-pandas ya proporciona una interfaz agradable que le permite pasar un marco de datos y solo usar un subconjunto de los datos y transformar arbitrariamente columnas individuales.

Si todo esto está en una transformación, entonces realmente no afecta cómo funciona sklearn por defecto.

No creo que se pueda implementar bien como transformador. Podría ser
uno o más metaestimadores o mixins. Creo que deberían ser inicialmente
implementado externamente y demostrado como útil

El 22 de octubre de 2015 a las 17:40, naught101 [email protected] escribió:

Sí, fuera de mi cabeza:

  • el índice puede ser realmente útil, por ejemplo, para crear retrasos cronometrados
    variables (por ejemplo, 1 día de retraso, en datos diarios con algunos días faltantes)
  • Los regresores sklearn podrían usarse de forma transparente con datos categóricos
    (pasar marcos de datos mixtos, transformar columnas categóricas con LabelBinarizer,
    el inverso_transformalo de nuevo).
  • sklearn-pandas ya proporciona una interfaz agradable que te permite
    pasar un marco de datos, y solo usar un subconjunto de los datos, y arbitrariamente
    transformar columnas individuales.

Si todo esto está en una transformación, entonces realmente no afecta cómo sklearn
funciona por defecto.

-
Responda a este correo electrónico directamente o véalo en GitHub
https://github.com/scikit-learn/scikit-learn/issues/5523#issuecomment -150123228
.

Mejorar "pandas in" fue una especie de idea detrás del transformador de columna PR # 3886. Tal vez debería haber examinado más de cerca lo que sklearn-pandas ya está haciendo. No estoy del todo seguro de cuál es la mejor manera de avanzar.

La otra cosa que estaría bien sería preservar los nombres de las columnas en las transformaciones / seleccionarlas al realizar la selección de características. No encuentro el problema en el que discutimos esto en este momento. Quizás @jnothman lo recuerde. Realmente me gustaría eso, aunque requeriría una cirugía mayor con la validación de entrada para preservar los nombres de las columnas: - /

Relacionado # 4196

aunque requeriría una cirugía mayor con la validación de entrada para
preservar los nombres de las columnas: - /

No solo validación de entrada: cada transformación tendría que describir lo que
hace a las columnas de entrada.

Es cierto, pero creo que estaría bien;)

Una pregunta es quizás si queremos esto solo en las tuberías o en todas partes. Si lo restringimos a los pipelines, la cirugía de validación de entrada sería menos grande. Pero no estoy seguro de cuán útil sería.

Siempre puedes hacer un pipeline con solo una cosa, ¿verdad? Así que, en cierto modo, manejamos todos los casos (aunque es hacky en el límite de 1 objeto) al restringir solo a la canalización al principio ...

+1. Comenzar con la tubería suena bien y cubrir todos los transformadores en el siguiente paso.

También tengo un impl con pandas y la integración de sklearn, que puede revertir la información de las columnas a través de inverse_transform (aunque hackear sucio ...)

http://pandas-ml.readthedocs.org/en/latest/sklearn.html

• el índice puede ser realmente útil, por ejemplo, para crear variables retrasadas cronometradas
(p. ej., retraso de 1 día, en datos diarios con algunos días faltantes)

Soy un poco estúpido, pero no estoy hablando de algo en la muestra.
dirección aquí, en lugar de la dirección de la característica?

• Los regresores sklearn podrían usarse de forma transparente con datos categóricos (pasar
marco de datos mixto, transforme columnas categóricas con LabelBinarizer, el
inverse_transform it back).

• sklearn-pandas ya proporciona una interfaz agradable que le permite pasar un
marco de datos, y solo usa un subconjunto de los datos, y transforma arbitrariamente
columnas individuales.

Está bien, pero eso es todo al nivel de un transformador que incorpora a Pandas,
y da una matriz de datos, ¿no? En lugar de intentar un
modificación en todos los objetos de scikit-learn (que es un riesgo
esfuerzo), primero podríamos implementar este transformador (creo que
@amueller tiene esto en mente).

muestra la dirección aquí, en lugar de la dirección de la característica?

Sí.

Está bien, pero eso es todo al nivel de un transformador que toma Pandas y da una matriz de datos, ¿no es así?

Sí, eso es lo que estaba pensando al principio. Estaría más que feliz con un contenedor que tratara con X y y como marcos de datos. No veo una razón obvia para jugar con los aspectos internos de sklearn.

OK, but that's all at the level of one transformer that takes Pandas in,
and gives a data matrix out, isn't it?

Sí, eso es lo que estaba pensando al principio. Estaría más que feliz con
un contenedor que trata con X e y como marcos de datos. No veo una razón obvia
joder con los componentes internos de sklearn.

Entonces estamos en la misma página. Creo que @amueller tiene ideas sobre
esto, y podríamos ver algo de discusión, y tal vez código pronto.

La otra cosa que estaría bien sería preservar los nombres de las columnas en las transformaciones / seleccionarlas al realizar la selección de características. No encuentro el problema en el que discutimos esto en este momento.

5172

Una nota: me había preguntado si uno solo querría envolver el estimador más externo en un conjunto para proporcionar esta funcionalidad a un usuario. Creo que la respuesta es: no, uno también quiere envolver transformadores atómicos, para permitir transformadores con reconocimiento de marco de datos dentro de una tubería (¿por qué no?). Sin implementar esto como un mixin, creo que tendrá problemas con el prefijo de parámetros innecesario o problemas de clonación (como en # 5080).

: +1:

Solo quería descartar la solución que estoy usando:

def check_output(X, ensure_index=None, ensure_columns=None):
    """
    Joins X with ensure_index's index or ensure_columns's columns when avaialble
    """
    if ensure_index is not None:
        if ensure_columns is not None:
            if type(ensure_index) is pd.DataFrame and type(ensure_columns) is pd.DataFrame:
                X = pd.DataFrame(X, index=ensure_index.index, columns=ensure_columns.columns)
        else:
            if type(ensure_index) is pd.DataFrame:
                X = pd.DataFrame(X, index=ensure_index.index)
    return X

Luego creo envoltorios alrededor de los estimadores de sklearn que llaman a esta función en la salida de la transformación, por ejemplo,

from sklearn.preprocessing import StandardScaler as _StandardScaler 
class StandardScaler(_StandardScaler):
    def transform(self, X):
        Xt = super(StandardScaler, self).transform(X)
        return check_output(Xt, ensure_index=X, ensure_columns=X)

Los clasificadores que necesitan usar el índice del marco de datos de entrada X pueden simplemente usar su índice (útil para series temporales como se señaló).

Este enfoque tiene la ventaja de ser completamente compatible con el diseño sklearn existente y al mismo tiempo preservar la velocidad de cálculo (las operaciones matemáticas y la indexación en marcos de datos son hasta 10 veces más lentas que las matrices numpy, http://penandpants.com/2014/09/05 / rendimiento-de-pandas-series-vs-numpy-arrays /). Desafortunadamente, es mucho trabajo tedioso agregar a cada estimador que podría utilizarlo.

Quizás solo sea necesario hacer una variante de Pipeline con esta magia ...

El 15 de enero de 2016 a las 02:30, Dean Wyatte [email protected] escribió:

Solo quería descartar la solución que estoy usando:

def check_output (X, asegurar_index = Ninguno, asegurar_columns = Ninguno):
"" "
Se une a X con el índice de secure_index o las columnas de sure_columns cuando esté disponible
"" "
si sure_index no es None:
si asegurar_columnas no es Ninguno:
si el tipo (asegurar_index) es pd.DataFrame y el tipo (asegurar_columnas) es pd.DataFrame:
X = pd.DataFrame (X, índice = índice_aseguro.index, columnas = columnas_aseguradas.columnas)
demás:
si el tipo (índice_aseguro) es pd.DataFrame:
X = pd.DataFrame (X, índice = índice_aseguro.index)
volver X

Luego creo envoltorios alrededor de los estimadores de sklearn que llaman a esta función
en la salida de la transformación, por ejemplo,

desde sklearn.preprocessing importar StandardScaler como _StandardScaler
clase MinMaxScaler (_MinMaxScaler):
def transform (self, X):
Xt = super (MinMaxScaler, self) .transform (X)
return salida_comprobación (Xt, índice_aseguro = X, columnas_aseguradas = X)

Los clasificadores que necesitan el uso del índice del marco de datos de entrada X pueden simplemente
use su índice (útil para series temporales como se señaló).

Este enfoque tiene la ventaja de ser completamente compatible con el
diseño sklearn existente al mismo tiempo que se preserva la velocidad de cálculo
(Las operaciones matemáticas y la indexación en marcos de datos son hasta 10 veces más lentas que numpy
matrices). Desafortunadamente, es mucho trabajo tedioso agregar a cada estimador
que podría utilizarlo.

-
Responda a este correo electrónico directamente o véalo en GitHub
https://github.com/scikit-learn/scikit-learn/issues/5523#issuecomment -171674105
.

O simplemente algo que envuelve una tubería / estimador, ¿no?

Realmente no entiendo por qué llamarías a una función como esa "check_ *" cuando está haciendo mucho más que solo verificar ...

El 14 de enero de 2016 a las 10:45:44 am CST, Joel Nothman [email protected] escribió:

Quizás solo sea necesario hacer una variante de Pipeline con esta magia ...

El 15 de enero de 2016 a las 02:30, Dean Wyatte [email protected]
escribió:

Solo quería descartar la solución que estoy usando:

def check_output (X, asegurar_index = Ninguno, asegurar_columns = Ninguno):
"" "
Se une a X con el índice de secure_index o las columnas de sure_columns
cuando esté disponible
"" "
si sure_index no es None:
si asegurar_columnas no es Ninguno:
si el tipo (sure_index) es pd.DataFrame y
tipo (asegurar_columnas) es pd.DataFrame:
X = pd.DataFrame (X, index = asegurar_index.index,
columnas = asegurar_columnas.columnas)
demás:
si el tipo (índice_aseguro) es pd.DataFrame:
X = pd.DataFrame (X, índice = índice_aseguro.index)
volver X

Luego creo envoltorios alrededor de los estimadores de sklearn que llaman a esto
función
en la salida de la transformación, por ejemplo,

desde sklearn.preprocessing importar StandardScaler como _StandardScaler
clase MinMaxScaler (_MinMaxScaler):
def transform (self, X):
Xt = super (MinMaxScaler, self) .transform (X)
return salida_comprobación (Xt, índice_aseguro = X, columnas_aseguradas = X)

Los clasificadores que necesitan el uso del índice del marco de datos de entrada X pueden
solo
use su índice (útil para series temporales como se señaló).

Este enfoque tiene la ventaja de ser completamente compatible con el
diseño sklearn existente al mismo tiempo que se preserva la velocidad de
cálculo
(las operaciones matemáticas y la indexación en marcos de datos son hasta 10 veces más lentas que
numpy
matrices). Desafortunadamente, es mucho trabajo tedioso agregar a cada
estimador
que podría utilizarlo.

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

https://github.com/scikit-learn/scikit-learn/issues/5523#issuecomment -171674105
.


Responda a este correo electrónico directamente o véalo en GitHub:
https://github.com/scikit-learn/scikit-learn/issues/5523#issuecomment -171697542

Enviado desde mi dispositivo Android con K-9 Mail. Por favor, disculpe mi brevedad.

No estoy seguro de si Pipeline es el lugar correcto para comenzar porque toda la herencia de nombres de columna es específica del estimador, por ejemplo, los escaladores deben heredar los nombres de columna del marco de datos de entrada, mientras que los modelos como PCA no deberían. Los estimadores de selección de características deberían heredar nombres de columna específicos, pero ese es otro problema, probablemente más relacionado con # 2007.

¿Es siempre el caso de que n_rows de todas las matrices se conservan durante la transformación? Si es así, simplemente heredar el índice de la entrada (si existe) parece seguro, pero no estoy seguro de que obtener un marco de datos con nombres de columna predeterminados (por ejemplo, [0, 1, 2, 3, ...]) sea mejor que el comportamiento actual desde la perspectiva del usuario final, pero si se utiliza un contenedor / metaestimador explícito, al menos el usuario sabrá qué esperar.

Además, acordé que check_ * es un nombre deficiente: estaba haciendo un poco más de validación en mi función y simplemente eliminé la lógica del marco de datos para publicar aquí.

Creo que la canalización sería el lugar para comenzar, aunque tendríamos que agregar algo a todos los estimadores que mapeen los nombres de las columnas de manera adecuada.

Los transformadores deben generar columnas con el nombre apropiado @ naught101

aunque requeriría una cirugía mayor con la validación de entrada para preservar los nombres de las columnas: - / @amueller

No solo validación de entrada: cada transformación tendría que describir lo que hace a las columnas de entrada. @GaelVaroquaux

¿Alguien ha pensado en la mecánica de cómo pasar los nombres, de transformador a transformador, y quizás cómo rastrear la procedencia? ¿Dónde guardaría uno esto?

Un amigo mío, @cbrummitt , tiene un problema similar, donde cada columna de su matriz de diseño es una forma funcional (por ejemplo, x ^ 2, x ^ 3, x_1 ^ 3x_2 ^ 2, representadas como expresiones sintéticas), y tiene transformadores que actúan de manera similar a PolynomialFeatures, que pueden tomar formas funcionales y generar más basadas en eso. Pero está usando sympy para tomar las expresiones antiguas y generar otras nuevas, y almacenar las expresiones como etiquetas de cadena no es suficiente y se complica cuando se superponen las transformaciones de funciones. Podría hacer todo esto fuera de la tubería, pero luego no obtiene el beneficio de GridSearch, etc.

Supongo que la versión más general de nuestra pregunta es, ¿cómo tiene alguna información que se pasaría de un transformador a otro que NO son los datos en sí? No puedo encontrar una excelente manera sin tener un estado global de tubería o que cada transformador / estimador sepa sobre los anteriores, o que cada paso devuelva varias cosas, o algo así.

Luego también se nos ocurrió la idea de modificar la canalización para realizar un seguimiento de esto, tendrías que cambiar _fit () y _transform () y quizás algunas otras cosas. Esa parece nuestra mejor opción.

Esto suena loco, pero lo que se siente es que realmente queremos que nuestra matriz de datos sea expresiones sintéticas, y cada transformación genera nuevas expresiones. Esto es asqueroso, check_array () evita que suceda y haría enojar a otros pasos en la tubería.

vea # 6425 para la idea actual.

Todo lo que desea es un mapeo, para cada transformador (incluida una tubería de
transformadores), desde los nombres de las funciones de entrada hasta los nombres de las funciones de salida (o algunos
representación estructurada de las transformaciones, que sospecho es más
ingeniería de lo que vamos a conseguir). Eso es lo que proporciona el # 6425.

El 8 de octubre de 2016 a las 03:42, Andreas Mueller [email protected]
escribió:

ver # 6425 https://github.com/scikit-learn/scikit-learn/issues/6425 para
la idea actual.

-
Recibes esto porque te mencionaron.
Responda a este correo electrónico directamente, véalo en GitHub
https://github.com/scikit-learn/scikit-learn/issues/5523#issuecomment -252301608,
o silenciar el hilo
https://github.com/notifications/unsubscribe-auth/AAEz65fBsMwqmkDq3DEjMSUC-W-nfn9zks5qxnZxgaJpZM4GThGc
.

¡Veremos esto, gracias!

¿Alguien puede proporcionar una actualización general sobre el estado del mundo con este problema?

¿El soporte de pandas DataFrame siempre será algo de YMMV?
Sería útil una guía sobre lo que se considera o no se considera seguro para su uso con pandas DataFrame lugar de solo ndarray . Quizás algo parecido a lo siguiente (TABLA DE EJEMPLO CREADA):

módulo / categoría | puede consumir pandas DataFrame de forma segura
- | -
sklearn.pipeline | SEGURO
sklearn.feature_selection | SEGURO
regresores | YMMV
sklearn.feature_extraction | NO ES SEGURO, no hay plan para implementar
etc | ...

En este momento, no estoy seguro de un enfoque que no sea "simplemente pruébelo y vea si arroja excepciones".

Hemos probado un puñado de ejemplos codificados a mano que parecen funcionar bien al aceptar un DataFrame de pandas, pero no podemos evitar pensar que esto inevitablemente dejará de funcionar justo cuando decidamos que necesitamos hacer un intercambio de componentes de canalización aparentemente trivial ... momento en el que todo se derrumba como un castillo de naipes en un rastro de pila críptico.

Mi proceso de pensamiento inicial fue crear un objeto de tubería de reemplazo que pueda consumir pandas DataFrame que generen automáticamente envoltorios para componentes estándar de scikit-learn para convertir los objetos de entrada / salida DataFrame en numpy ndarray objetos según sea necesario. De esa manera puedo escribir mis propios Selectores / Transformadores personalizados para poder usar pandas DataFrame primitivas, pero eso parece un poco torpe. Especialmente si estamos a punto de tener soporte "oficial" para ellos.

He estado siguiendo algunos PR diferentes, pero es difícil tener una idea de cuáles se abandonaron y / o cuáles reflejan el pensamiento actual:
Ejemplo:

6425 (referenciado octubre de 2016 arriba en este hilo)

9012 (superposiciones obvias con sklearn-pandas, pero anotado como experimental?)

3886 (¿reemplazado por # 9012?)

Esto depende fundamentalmente de lo que quiere decir con "Puede consumir pandas DataFrame de forma segura". Si te refieres a un DataFrame que contiene solo números flotantes, te garantizamos que todo funcionará. Si hay una sola cuerda en cualquier lugar, nada funcionará.

Creo que cualquier estimador de scikit-learn que devuelva un marco de datos para cualquier operación no trivial (o incluso trivial) es algo que podría no suceder nunca (aunque le gustaría).

9012 sucederá y se volverá estable, el PR es una primera iteración (o décima iteración, si cuenta las no fusionadas;)

Es probable que ocurra 6425, aunque no está completamente relacionado con los pandas.

3886 es de hecho reemplazado por # 9012

La funcionalidad # 6425 está actualmente implementada (para algunos transformadores y
extensible a otros) a través de singledispatch en
https://codecov.io/gh/TeamHG-Memex/eli5 por lo que vale.

El 21 de junio de 2017 a las 13:25, Andreas Mueller [email protected] escribió:

9012 https://github.com/scikit-learn/scikit-learn/pull/9012

sucederá y se estabilizará, el PR es una primera iteración.

6425 https://github.com/scikit-learn/scikit-learn/issues/6425 es

probable que suceda, aunque no está completamente relacionado con los pandas.

3886 https://github.com/scikit-learn/scikit-learn/pull/3886 es de hecho

reemplazado por # 9012
https://github.com/scikit-learn/scikit-learn/pull/9012

-
Recibes esto porque te mencionaron.
Responda a este correo electrónico directamente, véalo en GitHub
https://github.com/scikit-learn/scikit-learn/issues/5523#issuecomment-309952467 ,
o silenciar el hilo
https://github.com/notifications/unsubscribe-auth/AAEz61lgGBW1AoukPm_87elBjF2NGOUwks5sGI0-gaJpZM4GThGc
.

Ah, y cuando digo "Si te refieres a un DataFrame que contiene solo números flotantes, te garantizamos que todo funcionará". Me refiero a la indexación de columnas basada en la ubicación. Se supone que las columnas del conjunto de pruebas y de entrenamiento son las mismas por posición.

Esto depende fundamentalmente de lo que quiere decir con "Puede consumir pandas DataFrame de forma segura". Si te refieres a un DataFrame que contiene solo números flotantes, te garantizamos que todo funcionará. Si hay una sola cuerda en cualquier lugar, nada funcionará.

Creo que eso es suficiente para nosotros.

Estamos utilizando una canalización de componentes personalizados (envoltorios delgados alrededor de las herramientas existentes que no son compatibles con la canalización) para convertir tipos mixtos (cadenas, flotantes y entradas) en flotantes mediante codificación / escalado antes de llegar a componentes de scikit-learn como selectores o modelos.

Todos mis transformadores devuelven DataFrame s cuando se les da DataFrame s.
Cuando ingreso un DataFrame 300 columnas en un Pipeline y recibo un ndarray 500 columnas, no puedo aprender mucho de él de manera efectiva, por ejemplo, feature_selection , porque ya no tengo los nombres de las columnas. Si, digamos, mutual_info_classif me dice que solo las columnas 30 y 75 son importantes, no puedo averiguar cómo simplificar mi Pipeline para la producción.
Por lo tanto, es fundamental para mi caso de uso mantener mis datos en un DataFrame .
Gracias.

@ sam-s estoy totalmente de acuerdo. En el "corto" plazo, esto será abordado por https://github.com/scikit-learn/scikit-learn/pull/13307 y https://github.com/scikit-learn/enhancement_proposals/pull/18

No obtendrá un marco de datos de pandas, pero obtendrá el nombre de la columna para crear uno.

Sin embargo, ¿podría dar un ejemplo más concreto? Porque si todos los transformadores devuelven DataFrames, las cosas deberían funcionar (o hacerse que funcionen más fácilmente que las propuestas anteriores).

Ligera actualización a través de https://github.com/pandas-dev/pandas/issues/27211
lo que pone un freno a mis esperanzas. Parece que no podemos confiar en que haya un viaje de ida y vuelta sin copias, por lo que envolver y desenvolver en pandas resultará en un costo sustancial.

Ligera actualización a través de pandas-dev / pandas # 27211 que pone un freno a mis esperanzas. Parece que no podemos confiar en que haya un viaje de ida y vuelta sin copias, por lo que envolver y desenvolver en pandas resultará en un costo sustancial.

sí, pero supongo que una vez que cubramos la característica y los accesorios de muestra (los nombres de las filas y los "índices" son una especie de accesorio de muestra), la mayoría de los casos de uso relacionados que necesitan pandas ahora estarán cubiertos, ¿verdad?

@adrinjalali No estoy seguro de a qué te refieres con "la mayoría de los casos de uso relacionados con pandas de necesidad". Vi este problema no principalmente como el apoyo a los pandas para implementar funciones dentro de scikit-learn, sino para que scikit-learn se integre más fácilmente en un flujo de trabajo basado en pandas.

Solo por curiosidad, ¿existe un período de tiempo dentro del cual se espera que llegue la compatibilidad mejorada con Pandas? Estoy específicamente interesado en Pandas in -> Pandas out por StandardScaler .

Tengo un caso de uso en el que necesito marcos de datos de pandas conservados a través de cada paso en un Pipeline . Por ejemplo, una canalización con 1) paso de selección de características filtrando características basadas en datos, 2) paso de transformación de datos, 3) otro paso de selección de características para filtrar por nombres de columnas de características específicas o índices originales, 4) estandarización, 5) clasificación.

Paso 3) Creo que actualmente no es posible en sklearn, incluso con una entrada de matriz numerosa, porque los índices de características originales no tienen sentido cuando los datos llegan a 3) ya que en 1) hubo un paso de selección de características. Si los marcos de datos de pandas se conservaran en la canalización, funcionaría porque podría filtrar por nombre de columna en 3).

¿Me equivoco al pensar que actualmente no hay forma de hacer esto incluso con una gran cantidad de entradas de matriz?

Tienes razón en que no es compatible, y respaldarlo no sería trivial. En relación con su caso de uso, estamos trabajando para pasar los nombres de las funciones a lo largo de la canalización (como puede ver en los PR vinculados y las propuestas anteriores). Con suerte, eso debería ayudar con su caso una vez que esté hecho. No estoy seguro de si ayuda, pero también puedes echarle un vistazo a https://github.com/scikit-learn-contrib/sklearn-pandas

Tienes razón en que no es compatible, y respaldarlo no sería trivial. En relación con su caso de uso, estamos trabajando para pasar los nombres de las funciones a lo largo de la canalización (como puede ver en los PR vinculados y las propuestas anteriores). Con suerte, eso debería ayudar con su caso una vez que esté hecho.

Gracias por la confirmación, sí, poder pasar los nombres de las funciones (u otras propiedades de las funciones) para ajustar los métodos y dividirlos correctamente durante cada paso de selección de funciones estaría bien para este caso de uso.

No estoy seguro de si ayuda, pero también puedes echarle un vistazo a https://github.com/scikit-learn-contrib/sklearn-pandas

Anteriormente leí sus documentos y tal vez no los esté viendo, pero la mayoría (o todas) de sus funciones están obsoletas ahora en scikit-learn 0.21 con sklearn.compose.ColumnTransformer ? Además, no parece que sean compatibles con pandas, ya que parece matrices numpy después de transformaciones.

(Me pregunto si admitir Pandas en la selección de funciones se rompería
mucho...)

Solo revisando brevemente el código, hay todo tipo de verificaciones que ocurren arbitrariamente en muchos lugares, usando por ejemplo https://github.com/scikit-learn/scikit-learn/blob/939fa3cccefe708db7a81c5248db32a1d600bf8d/sklearn/utils/validation.py# L619

Además, muchas operaciones usan la indexación de una manera numerosa que no sería aceptada por el marco de datos de pandas.

Mantener a los pandas adentro / afuera sería una necesidad para la ciencia de datos del día a día, en mi opinión, pero scikit-learn parece estar diseñado de una manera que dificultaría su implementación.

Mantener a los pandas dentro / fuera sería una necesidad para la ciencia de datos del día a día, en mi opinión, pero
scikit-learn parece estar diseñado de una manera que haría difícil ser
implementado.

Los buenos números son difíciles de implementar en los marcos de datos de pandas. Son solo
no está destinado a eso, en particular para operaciones multivariantes (numérico
operaciones entre columnas).

El aprendizaje automático es principalmente numérico multivariante.

Los buenos números son difíciles de implementar en los marcos de datos de pandas. Simplemente no están diseñados para eso, en particular para operaciones multivariadas (operaciones numéricas entre columnas). El aprendizaje automático es principalmente numérico multivariante.

¿Esa decisión debe dejarse en manos del usuario? En mi experiencia en el uso extensivo de scikit-learn durante los últimos dos años, creo que faltan dos funcionalidades principales e importantes que son imprescindibles para muchos casos de uso de ML: la compatibilidad con la transferencia de metadatos de características y muestras. El soporte completo de marcos de datos de pandas es una forma natural y elegante de lidiar con algo de esto.

Este tipo de funcionalidades básicas son muy importantes para mantener la base de usuarios y atraer nuevos usuarios. De lo contrario, veo bibliotecas como, por ejemplo, mlr3 que eventualmente maduran y atraen a los usuarios fuera de sklearn porque sé que son (o serán) totalmente compatibles con marcos de datos y metadatos.

¿Esa decisión debe dejarse en manos del usuario?

Bueno, el usuario no está implementando el algoritmo.

De lo contrario, veo bibliotecas como, por ejemplo, mlr3 que finalmente maduran y
atraer usuarios fuera de sklearn porque sé que lo hacen (o lo harán)
Soporta completamente marcos de datos y metadatos.

mlr3 está en R, los marcos de datos son bastante diferentes del marco de datos de pandas.
Quizás esto lo haga más fácil de implementar.

Estoy de acuerdo en que un mejor soporte para nombres de funciones y datos heterogéneos
tipos es importante. Trabajamos para encontrar buenas soluciones técnicas.
que no provoquen una pérdida de rendimiento y un código demasiado complicado.

¿Esa decisión debe dejarse en manos del usuario?
Bueno, el usuario no está implementando el algoritmo.
De lo contrario, veo bibliotecas como, por ejemplo, mlr3 que eventualmente maduran y atraen a los usuarios fuera de sklearn porque sé que son (o serán) totalmente compatibles con marcos de datos y metadatos.
mlr3 está en R, los marcos de datos son bastante diferentes del marco de datos de pandas. Quizás esto lo haga más fácil de implementar. Estoy de acuerdo en que es importante un mejor soporte para nombres de funciones y tipos de datos heterogéneos. Estamos trabajando para encontrar buenas soluciones técnicas que no provoquen una pérdida de rendimiento y un código demasiado complicado.

Creo que su enfoque de ceñirse a matrices numpy y al menos admitir nombres de funciones de paso o incluso mejor, metadatos de funciones múltiples funcionaría para muchos casos de uso. Para pasar metadatos de muestra de entrenamiento, ya lo admite en **fit_params y sé que hay un esfuerzo para mejorar el diseño. Pero mencioné en https://github.com/scikit-learn/enhancement_proposals/pull/16 que hay casos de uso en los que también necesitaría metadatos de muestra de prueba pasados ​​a métodos transform y esto no es compatible actualmente .

mlr3 está en R, los marcos de datos son bastante diferentes del marco de datos de pandas.

Los científicos computacionales en la investigación de las ciencias de la vida generalmente se sienten muy cómodos con Python y R y usan ambos juntos (incluido yo mismo). Estoy bastante seguro de que un porcentaje significativo de la base de usuarios de scikit-learn son investigadores de ciencias biológicas.

Actualmente, las bibliotecas de ML maduras disponibles en R En mi humilde opinión ni siquiera se acercan a scikit-learn en términos de proporcionar una API bien diseñada y hacer que las partes utilitarias de ML sean muy sencillas (canalizaciones, búsqueda de hiperparámetros, puntuación, etc.) mientras que en R con estas bibliotecas tienes que codificarlo tú mismo. Pero mlr3 lo veo como una gran competencia futura para scikit-learn, ya que lo están diseñando desde cero de la manera correcta.

Los buenos números son difíciles de implementar en los marcos de datos de pandas. Son solo
no está destinado a eso, en particular para operaciones multivariantes (numérico
operaciones entre columnas).

Tal vez me esté perdiendo algo, pero ¿no sería posible desenvolver el DataFrame (usando df.values ), hacer los cálculos y luego volver a un nuevo DataFrame?

Eso es básicamente lo que hago manualmente entre los pasos, y lo único que previene el uso de Pipeline .

Tal vez me esté perdiendo algo, pero ¿no sería posible desenvolver el
DataFrame (usando df.values), haga los cálculos y luego vuelva a un nuevo
Marco de datos ?

En general no: puede que no funcione (columnas heterogéneas) y funcionará
dar lugar a muchas copias de la memoria.

En general no: puede que no funcione (columnas heterogéneas)

Creo que Column Transformers y demás pueden manejarlo indicidualmente.

dará lugar a muchas copias de la memoria.

Entiendo que hay que tomar decisiones de diseño e implementación difíciles, y ese es un argumento sólido.

Sin embargo, no entiendo por qué argumentaría que no es una buena idea mejorar la forma en que sklearn admite los metadatos de columna.

Permitir, por ejemplo, ingerir un df con características, agregar una columna gracias a un predictor, hacer más manipulaciones de datos, hacer otra predicción, todo eso en un Pipeline, es algo que sería útil porque (por ejemplo) permitiría la optimización de hiperparámetros de una manera mucho mejor integrada y elegante.

Hacerlo con o sin pandas es solo una sugerencia ya que es la forma más común, fácil y popular de manipular datos, y no veo ningún beneficio en reescribir más de lo que hicieron.

Dependería del usuario decidir no utilizar este flujo de trabajo al optimizar el rendimiento.

Dejar que el usuario decida las cosas requiere una explicación clara del
elección del usuario. La mayoría de los usuarios no leen la documentación que
explicar tales elecciones. Muchos intentarían lo que creen que podría funcionar, y luego
darse por vencidos cuando lo encuentran lento, sin darse cuenta de que fue su elección de
daraframe que lo hizo así.

Así que tenemos que dar un paso con algo de cuidado aquí. Pero tenemos que seguir resolviendo
esto como una alta prioridad.

Creo que la mejor solución sería admitir marcos de datos de pandas dentro y fuera de la muestra y las propiedades de características y pasarlos y dividirlos correctamente en tren y prueba de ajuste / transformación. Eso resolvería la mayoría de los casos de uso manteniendo la velocidad de la matriz de datos X como matrices numerosas.

Un punto importante que falta en estos argumentos es que los pandas se están moviendo hacia una representación en columnas de los datos, de manera que np.array(pd.DataFrame(numpy_data)) tendrá dos copias de memoria _garantizadas_. Es por eso que no es tan fácil como mantener el marco de datos y usar values siempre que necesitemos velocidad.

Un punto importante que falta en estos argumentos es que los pandas se están moviendo hacia una representación en columnas de los datos, de manera que np.array(pd.DataFrame(numpy_data)) tendrá dos copias de memoria _garantizadas_. Es por eso que no es tan fácil como mantener el marco de datos y usar values siempre que necesitemos velocidad.

Espero haber sido claro en mi anterior post. Creo que scikit-learn actualmente no necesita admitir marcos de datos pandas para datos X, manténgalos como matrices rápidas y numpy. Pero lo que resolvería muchos casos de uso es el soporte completo a través del marco para marcos de datos de pandas para metadatos, es decir, propiedades de muestra y propiedades de características. Esto no debería ser una carga de rendimiento incluso para las copias de memoria, ya que estas dos estructuras de datos serán menores en comparación con X y, en realidad, solo se realizarán subconjuntos en ellas.

Sí, esos cambios ayudan en muchos casos de uso y estamos trabajando en ellos. Pero este problema va más allá de eso: https://github.com/scikit-learn/scikit-learn/issues/5523#issuecomment -508807755

@hermidalc, ¿estás sugiriendo que dejemos que X sea ​​una matriz numerosa y asignemos los metadatos en otros objetos de marco de datos?

@hermidalc, ¿estás sugiriendo que dejemos que X sea ​​una matriz numerosa y asignemos los metadatos en otros objetos de marco de datos?

Sí, soporte completo para propiedades de muestra y propiedades de características como marcos de datos de pandas. Ya se está discutiendo sobre propiedades de muestra y nombres de características en otros RP y problemas, por ejemplo, aquí # 9566 y # 14315

Leí sobre este tema y parece que hay dos bloqueadores principales aquí:

  1. https://github.com/pandas-dev/pandas/issues/27211
  2. Que pandas no maneja matrices ND.

¿Ha considerado agregar soporte para xarrays en su lugar? No tienen las limitaciones de los pandas.

X = np.arange(10).reshape(5, 2)
assert np.asarray(xr.DataArray(X)) is X
assert np.asarray(xr.Dataset({"data": (("samples", "features"), X)}).data).base is X.base

Hay un paquete llamado sklearn-xarray : https://phausamann.github.io/sklearn-xarray/content/wrappers.html que envuelve los estimadores de scikit para manejar xarrays como entrada y salida, pero parece que no se ha mantenido durante años. Sin embargo, me pregunto si los envoltorios son el camino a seguir aquí.

xarray se está considerando activamente. Se está creando un prototipo y se está trabajando en él aquí: https://github.com/scikit-learn/scikit-learn/pull/16772 Hay un cuaderno de uso sobre cómo se vería la API en el PR.

(Volveré a él después de que terminemos con la versión 0.23)

También estoy muy interesado en esta función.
Resolvería infinitos problemas. Actualmente esta es la solución que estoy usando.
Escribí un contenedor alrededor del módulo sklearn.preprocessing , al que llamé sklearn_wrapper

Entonces, en lugar de importar desde sklearn.preprocessing , importo desde sklearn_wrapper .
Por ejemplo:

# this
from sklearn.preprocessing import StandardScaler 
# becomes 
from sklearn_wrapper import StandardScaler

A continuación la implementación de este módulo. Pruébelo y déjeme saber lo que piensan

from functools import wraps
from itertools import chain

import pandas as pd
from sklearn import preprocessing, compose, feature_selection, decomposition
from sklearn.compose._column_transformer import _get_transformer_list

modules = (preprocessing, feature_selection, decomposition)


def base_wrapper(Parent):
    class Wrapper(Parent):

        def transform(self, X, **kwargs):
            result = super().transform(X, **kwargs)
            check = self.check_out(X, result)
            return check if check is not None else result

        def fit_transform(self, X, y=None, **kwargs):
            result = super().fit_transform(X, y, **kwargs)
            check = self.check_out(X, result)
            return check if check is not None else result

        def check_out(self, X, result):
            if isinstance(X, pd.DataFrame):
                result = pd.DataFrame(result, index=X.index, columns=X.columns)
                result = result.astype(X.dtypes.to_dict())
            return result

        def __repr__(self):
            name = Parent.__name__
            tmp = super().__repr__().split('(')[1]
            return f'{name}({tmp}'

    Wrapper.__name__ = Parent.__name__
    Wrapper.__qualname__ = Parent.__name__

    return Wrapper


def base_pca_wrapper(Parent):
    Parent = base_wrapper(Parent)

    class Wrapper(Parent):
        @wraps(Parent)
        def __init__(self, *args, **kwargs):
            self._prefix_ = kwargs.pop('prefix', 'PCA')
            super().__init__(*args, **kwargs)

        def check_out(self, X, result):
            if isinstance(X, pd.DataFrame):
                columns = [f'{self._prefix_}_{i}' for i in range(1, (self.n_components or X.shape[1]) + 1)]
                result = pd.DataFrame(result, index=X.index, columns=columns)
            return result

    return Wrapper


class ColumnTransformer(base_wrapper(compose.ColumnTransformer)):

    def check_out(self, X, result):
        if isinstance(X, pd.DataFrame):
            return pd.DataFrame(result, index=X.index, columns=self._columns[0]) if self._remainder[1] == 'drop' \
                else pd.DataFrame(result, index=X.index, columns=X.columns). \
                astype(self.dtypes.iloc[self._remainder[-1]].to_dict())


class SelectKBest(base_wrapper(feature_selection.SelectKBest)):

    def check_out(self, X, result):
        if isinstance(X, pd.DataFrame):
            return pd.DataFrame(result, index=X.index, columns=X.columns[self.get_support()]). \
                astype(X.dtypes[self.get_support()].to_dict())


def make_column_transformer(*transformers, **kwargs):
    n_jobs = kwargs.pop('n_jobs', None)
    remainder = kwargs.pop('remainder', 'drop')
    sparse_threshold = kwargs.pop('sparse_threshold', 0.3)
    verbose = kwargs.pop('verbose', False)
    if kwargs:
        raise TypeError('Unknown keyword arguments: "{}"'
                        .format(list(kwargs.keys())[0]))
    transformer_list = _get_transformer_list(transformers)
    return ColumnTransformer(transformer_list, n_jobs=n_jobs,
                             remainder=remainder,
                             sparse_threshold=sparse_threshold,
                             verbose=verbose)


def __getattr__(name):
    if name not in __all__:
        return

    for module in modules:
        Parent = getattr(module, name, None)
        if Parent is not None:
            break

    if Parent is None:
        return

    if module is decomposition:
        Wrapper = base_pca_wrapper(Parent)
    else:
        Wrapper = base_wrapper(Parent)

    return Wrapper


__all__ = [*[c for c in preprocessing.__all__ if c[0].istitle()],
           *[c for c in decomposition.__all__ if c[0].istitle()],
           'SelectKBest']


def __dir__():
    tmp = dir()
    tmp.extend(__all__)
    return tmp

https://github.com/koaning/scikit-lego/issues/304 proporcionó otra solución mediante Hot-fixing en sklearn.pipeline.FeatureUnion

¿Fue útil esta página
0 / 5 - 0 calificaciones