Scikit-learn: Des pandas dedans, des pandas dehors ?

Créé le 22 oct. 2015  ·  59Commentaires  ·  Source: scikit-learn/scikit-learn

Pour le moment, il est possible d'utiliser une trame de données pandas comme entrée pour la plupart des méthodes sklearn fit/predict/transform, mais vous obtenez un tableau numpy. Ce serait vraiment bien de pouvoir sortir les données dans le même format que celui dans lequel vous les avez mises.

Ce n'est pas tout à fait simple, car si votre Dataframe contient des colonnes qui ne sont pas numériques, les tableaux numpy intermédiaires entraîneront l'échec de sklearn, car ils seront dtype=object , au lieu de dtype=float . Cela peut être résolu en ayant un transformateur Dataframe->ndarray, qui mappe les données non numériques aux données numériques (par exemple, des entiers représentant des classes/catégories). sklearn-pandas le fait déjà, bien qu'il n'ait actuellement

J'ai l'impression qu'une transformation comme celle-ci serait _vraiment_ utile dans sklearn - c'est le genre de chose que toute personne travaillant avec des ensembles de données avec plusieurs types de données trouverait utile. Que faudrait-il pour obtenir quelque chose comme ça dans sklearn?

Commentaire le plus utile

Tous mes transformateurs renvoient DataFrame s lorsqu'ils reçoivent DataFrame s.
Lorsque j'entre un DataFrame 300 colonnes dans un Pipeline et que je reçois un ndarray 500 colonnes, je ne peux pas en tirer grand-chose, par exemple, feature_selection , car je n'ai plus les noms de colonnes. Si, disons, mutual_info_classif me dit que seules les colonnes 30 et 75 sont importantes, je n'arrive pas à comprendre comment simplifier mon Pipeline pour la production.
Il est donc essentiel pour mon cas d'utilisation de conserver mes données dans un DataFrame .
Merci.

Tous les 59 commentaires

Scikit-learn a été conçu pour fonctionner avec un format d'entrée très générique. Peut-être que le monde autour de scikit-learn a beaucoup changé depuis, d'une manière qui rend l'intégration de Pandas plus importante. Il pourrait encore être largement fourni par des wrappers tiers.

Mais en dehors de la question plus large, je pense que vous devriez essayer de donner des exemples de la façon dont la sortie compatible avec Pandas des estimateurs standard différera et fera une différence en termes de convivialité. Exemples auxquels je peux penser :

  • toutes les méthodes pourraient copier l'index de l'entrée
  • les transformateurs doivent sortir des colonnes nommées de manière appropriée
  • Predict_proba multiclasse peut étiqueter des colonnes avec des noms de classe

Ouais, du haut de ma tête:

  • l'index peut être très utile, par exemple pour créer des variables décalées dans le temps (par exemple, décalage d'un jour, sur des données quotidiennes avec quelques jours manquants)
  • Les régresseurs sklearn peuvent être utilisés de manière transparente avec des données catégorielles (transmettre une trame de données mixte, transformer des colonnes catégorielles avec LabelBinarizer, inverser_transformer).
  • sklearn-pandas fournit déjà une interface agréable qui vous permet de transmettre une trame de données, d'utiliser uniquement un sous-ensemble des données et de transformer arbitrairement des colonnes individuelles.

Si tout cela se trouve dans une transformation, cela n'affecte pas vraiment le fonctionnement par défaut de sklearn.

Je ne pense pas qu'il puisse être implémenté correctement en tant que transformateur. Ce serait
un ou plusieurs méta-estimateurs ou mixins. Je pense qu'ils devraient être au départ
mis en œuvre à l'externe et démontré comme utile

Le 22 octobre 2015 à 17h40, naught101 [email protected] a écrit :

Ouais, du haut de ma tête:

  • l'index peut être très utile, par exemple pour créer des retards temporisés
    variables (par exemple décalage 1 jour, sur les données quotidiennes avec quelques jours manquants)
  • les régresseurs sklearn pourraient être utilisés de manière transparente avec des données catégorielles
    (transmettre une trame de données mixte, transformer des colonnes catégorielles avec LabelBinarizer,
    l'inverse_transform le retour).
  • sklearn-pandas fournit déjà une interface agréable qui vous permet de
    passer une trame de données, et n'utiliser qu'un sous-ensemble des données, et arbitrairement
    transformer des colonnes individuelles.

Si tout cela est dans une transformation, cela n'affecte pas vraiment la façon dont sklearn
fonctionne par défaut.

-
Répondez directement à cet e-mail ou consultez-le sur GitHub
https://github.com/scikit-learn/scikit-learn/issues/5523#issuecomment -150123228
.

Améliorer les "pandas dedans" était en quelque sorte l'idée derrière le transformateur de colonne PR #3886. J'aurais peut-être dû regarder de plus près ce que sklearn-pandas fait déjà. Je ne suis pas tout à fait sûr de la meilleure voie à suivre.

L'autre chose qui serait bien serait de conserver les noms de colonnes dans les transformations / les sélectionner lors de la sélection de fonctionnalités. Je ne trouve pas le problème où nous avons discuté de cela en ce moment. Peut-être que @jnothman se souvient. J'aimerais vraiment ça, même si cela nécessiterait une intervention chirurgicale majeure avec la validation d'entrée pour préserver les noms de colonnes :-/

Connexes #4196

même si cela nécessiterait une intervention chirurgicale majeure avec la validation des entrées pour
conserver les noms de colonnes :-/

Non seulement la validation d'entrée : chaque transformation devrait décrire ce qu'elle
fait aux colonnes d'entrée.

C'est vrai, mais je pense que ce serait bien ;)

Une question est peut-être de savoir si nous voulons cela uniquement dans les pipelines ou partout. Si nous le restreignons aux pipelines, la chirurgie de validation des entrées serait moins importante. Mais je ne sais pas à quel point ce serait utile.

Vous pouvez toujours créer un pipeline avec une seule chose, n'est-ce pas ? Donc, nous gérons en quelque sorte tous les cas (bien que ce soit difficile dans la limite de 1 objet) en nous limitant au pipeline au début...

+1. Commencer par le pipeline semble bien et couvrir tous les transformateurs à l'étape suivante.

J'ai également un impl avec l'intégration de pandas et de sklearn, qui peut rétablir les informations sur les colonnes via inverse_transform (sale hack cependant...)

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

• l'index peut être très utile, par exemple pour créer des variables temporisées
(par exemple décalage 1 jour, sur les données quotidiennes avec quelques jours manquants)

Je suis un peu stupide, mais je ne parle pas de quelque chose dans l'échantillon
direction ici, plutôt que la direction de la fonction ?

• les régresseurs sklearn pourraient être utilisés de manière transparente avec des données catégorielles (passe
cadre de données mixte, transformez les colonnes catégorielles avec LabelBinarizer, le
inverse_transformez-le).

• sklearn-pandas fournit déjà une interface sympa qui vous permet de passer un
dataframe, et n'utilisez qu'un sous-ensemble des données, et transformez arbitrairement
colonnes individuelles.

OK, mais c'est tout au niveau d'un transformateur qui prend en charge les Pandas,
et donne une matrice de données, n'est-ce pas ? Plutôt que de tenter une
modification sur tous les objets de scikit-learn (ce qui est un
endeavior), nous pourrions d'abord implémenter ce transformateur (je crois que
@amueller a ça en tête).

la direction de l'échantillon ici, plutôt que la direction de la fonction ?

Ouais.

OK, mais c'est tout au niveau d'un transformateur qui prend Pandas et donne une matrice de données, n'est-ce pas ?

Oui, c'est ce que je pensais au départ. Je serais plus qu'heureux d'un wrapper qui traite X et y tant que dataframes. Je ne vois pas de raison évidente de visser les internes 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?

Oui, c'est ce que je pensais au départ. je serais plus que content de
un wrapper qui traitait X et y en tant que trames de données. je ne vois pas de raison évidente
à visser avec les internes de sklearn.

Ensuite, nous sommes sur la même page. Je pense que @amueller a des idées sur
cela, et nous pourrions voir une discussion, et peut-être du code bientôt.

L'autre chose qui serait bien serait de conserver les noms de colonnes dans les transformations / les sélectionner lors de la sélection de fonctionnalités. Je ne trouve pas le problème où nous avons discuté de cela en ce moment.

5172

Une note : je m'étais demandé si l'on voudrait seulement envelopper l'estimateur le plus externe dans un ensemble pour fournir cette fonctionnalité à un utilisateur. Je pense que la réponse est : non, on veut aussi envelopper des transformateurs atomiques, pour permettre des transformateurs prenant en charge les données dans un pipeline (pourquoi pas ?). Sans implémenter cela en tant que mixin, je pense que vous allez rencontrer des problèmes de préfixation de paramètres inutiles ou des problèmes de clonage (comme dans #5080).

:+1:

Je voulais juste jeter la solution que j'utilise:

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

Je crée ensuite des wrappers autour des estimateurs de sklearn qui appellent cette fonction sur la sortie de transform, par exemple,

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)

Les classificateurs qui ont besoin d'utiliser l'index de la trame de données d'entrée X peuvent simplement utiliser son index (utile pour les séries temporelles, comme cela a été souligné).

Cette approche a l'avantage d'être totalement compatible avec la conception sklearn existante tout en préservant la vitesse de calcul (les opérations mathématiques et l'indexation sur les dataframes sont jusqu'à 10 fois plus lentes que les tableaux numpy, http://penandpants.com/2014/09/05 /performance-of-pandas-series-vs-numpy-arrays/). Malheureusement, c'est beaucoup de travail fastidieux à ajouter à chaque estimateur qui pourrait l'utiliser.

Peut-être est-il seulement nécessaire de faire une variante de Pipeline avec cette magie...

Le 15 janvier 2016 à 02h30, Dean Wyatte [email protected] a écrit :

Je voulais juste jeter la solution que j'utilise:

def check_output(X, assurer_index=Aucun, assurer_columns=Aucun) :
"""
Joint X avec l'index d'assure_index ou les colonnes d'assure_columns lorsqu'il est disponible
"""
si assurer_index n'est pas Aucun :
si assure_columns n'est pas Aucun :
si type(ensure_index) est pd.DataFrame et type(ensure_columns) est pd.DataFrame :
X = pd.DataFrame(X, index=ensure_index.index, colonnes=ensure_columns.columns)
autre:
si le type (ensure_index) est pd.DataFrame :
X = pd.DataFrame(X, index=ensure_index.index)
retour X

Je crée ensuite des wrappers autour des estimateurs de sklearn qui appellent cette fonction
sur la sortie de transform, par exemple,

à partir de sklearn.preprocessing importer StandardScaler en tant que _StandardScaler
classe MinMaxScaler(_MinMaxScaler) :
def transform(self, X):
Xt = super(MinMaxScaler, self).transform(X)
renvoyer check_output(Xt, assurer_index=X, assurer_columns=X)

Les classificateurs qui ont besoin d'utiliser l'index de la trame de données d'entrée X peuvent simplement
utiliser son index (utile pour les séries temporelles comme cela a été souligné).

Cette approche a l'avantage d'être tout à fait compatible avec les
conception sklearn existante tout en préservant la vitesse de calcul
(les opérations mathématiques et l'indexation sur les dataframes sont jusqu'à 10 fois plus lentes que numpy
tableaux). Malheureusement, c'est beaucoup de travail fastidieux à ajouter à chaque estimateur
qui pourrait l'utiliser.

-
Répondez directement à cet e-mail ou consultez-le sur GitHub
https://github.com/scikit-learn/scikit-learn/issues/5523#issuecomment-171674105
.

Ou juste quelque chose qui enveloppe un pipeline/estimateur, non ?

Je ne comprends pas vraiment pourquoi vous appelez une fonction comme celle-ci "check_*" alors qu'elle fait bien plus que simplement vérifier...

Le 14 janvier 2016 à 10 h 45 min 44 s CST, Joel Nothman [email protected] a écrit :

Peut-être est-il seulement nécessaire de faire une variante de Pipeline avec cette magie...

Le 15 janvier 2016 à 02h30, Dean Wyatte [email protected]
a écrit:

Je voulais juste jeter la solution que j'utilise:

def check_output(X, assurer_index=Aucun, assurer_columns=Aucun) :
"""
Joint X avec l'index d'assure_index ou les colonnes d'assure_columns
quand disponible
"""
si assurer_index n'est pas Aucun :
si assure_columns n'est pas Aucun :
si le type (ensure_index) est pd.DataFrame et
type(ensure_columns) est pd.DataFrame :
X = pd.DataFrame(X, index=ensure_index.index,
colonnes=ensure_columns.columns)
autre:
si le type (ensure_index) est pd.DataFrame :
X = pd.DataFrame(X, index=ensure_index.index)
retour X

Je crée ensuite des wrappers autour des estimateurs de sklearn qui appellent ceci
fonction
sur la sortie de transform, par exemple,

à partir de sklearn.preprocessing importer StandardScaler en tant que _StandardScaler
classe MinMaxScaler(_MinMaxScaler) :
def transform(self, X):
Xt = super(MinMaxScaler, self).transform(X)
renvoyer check_output(Xt, assurer_index=X, assurer_columns=X)

Les classificateurs qui nécessitent l'utilisation de l'index de la trame de données d'entrée X peuvent
seulement
utiliser son index (utile pour les séries temporelles comme cela a été souligné).

Cette approche a l'avantage d'être tout à fait compatible avec les
conception sklearn existante tout en préservant la vitesse de
calcul
(les opérations mathématiques et l'indexation sur les trames de données sont jusqu'à 10 fois plus lentes que
numpy
tableaux). Malheureusement, c'est beaucoup de travail fastidieux à ajouter à chaque
estimateur
qui pourrait l'utiliser.

-
Répondez directement à cet e-mail ou consultez-le sur GitHub

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


Répondez directement à cet e-mail ou consultez-le sur GitHub :
https://github.com/scikit-learn/scikit-learn/issues/5523#issuecomment-171697542

Envoyé depuis mon appareil Android avec K-9 Mail. Veuillez excuser ma brièveté.

Je ne sais pas si Pipeline est le bon endroit pour commencer car tout l'héritage des noms de colonnes est spécifique à l'estimateur. Les estimateurs de sélection de caractéristiques devraient hériter de noms de colonnes spécifiques, mais c'est un autre problème, probablement plus lié à #2007.

Est-il toujours vrai que n_rows de tous les tableaux est préservé lors de la transformation ? Si tel est le cas, le simple fait d'hériter de l'index de l'entrée (s'il existe) semble sûr, mais je ne suis pas sûr qu'obtenir une trame de données avec des noms de colonne par défaut (par exemple, [0, 1, 2, 3, ...]) soit mieux que le comportement actuel du point de vue de l'utilisateur final, mais si un wrapper/méta-estimateur explicite est utilisé, alors au moins l'utilisateur saura à quoi s'attendre.

De plus, j'ai convenu que check_* est un mauvais nom - je faisais un peu plus de validation dans ma fonction et j'ai simplement supprimé la logique de la trame de données pour la publier ici.

Je pense que pipeline serait le point de départ, bien que nous devions ajouter quelque chose à tous les estimateurs qui mappent les noms de colonnes de manière appropriée.

les transformateurs doivent sortir des colonnes nommées de manière appropriée @ naught101

bien que cela nécessiterait une intervention chirurgicale majeure avec la validation d'entrée pour préserver les noms de colonnes :-/ @amueller

Pas seulement la validation d'entrée : chaque transformation devrait décrire ce qu'elle fait aux colonnes d'entrée. @GaelVaroquaux

Quelqu'un a-t-il pensé à la mécanique de la transmission des noms, de transformateur à transformateur, et peut-être à la manière de suivre la provenance ? Où stockerait-on cela ?

Un de mes amis, @cbrummitt , a un problème similaire, où chaque colonne de sa matrice de conception est une forme fonctionnelle (par exemple x^2, x^3, x_1^3x_2^2, représentée par des expressions sympy), et il a des transformateurs qui agissent de manière similaire à PolynomialFeatures, qui peuvent prendre des formes fonctionnelles et en générer d'autres sur cette base. Mais il utilise sympy pour prendre les anciennes expressions et en générer de nouvelles, et le stockage des expressions sous forme d'étiquettes de chaîne ne le coupe pas et se complique lorsque vous superposez les transformations de fonction. Il pourrait faire tout cela en dehors du pipeline, mais alors il ne bénéficie pas de GridSearch, etc.

Je suppose que la version plus générale de notre question est, comment avez-vous des informations qui seraient transmises de transformateur à transformateur qui ne sont PAS les données elles-mêmes? Je ne peux pas trouver un bon moyen sans avoir un état global du pipeline ou que chaque transformateur / estimateur connaisse les précédents, ou que chaque étape renvoie plusieurs choses, ou quelque chose.

Nous avons alors également eu l'idée de modifier le pipeline pour garder une trace de cela, vous devriez changer _fit() et _transform() et peut-être quelques autres choses. Cela semble être notre meilleure option.

Cela semble fou, mais on a l'impression que nous voulons vraiment que notre matrice de données soit des expressions sympy et que chaque transformation génère de nouvelles expressions ? C'est dégoûtant, check_array () l'empêche de se produire et cela rendrait les autres étapes du pipeline en colère.

voir #6425 pour l'idée actuelle.

Tout ce que vous voulez, c'est un mappage, pour chaque transformateur (y compris un pipeline de
transformateurs), des noms d'entités d'entrée aux noms d'entités de sortie (ou certains
représentation structurée des transformations, qui, je le soupçonne, est plus
l'ingénierie que nous allons obtenir). C'est ce que #6425 fournit.

Le 8 octobre 2016 à 03h42, Andreas Mueller [email protected]
a écrit:

voir #6425 https://github.com/scikit-learn/scikit-learn/issues/6425 pour
l'idée actuelle.

-
Vous recevez ceci parce que vous avez été mentionné.
Répondez directement à cet e-mail, consultez-le sur GitHub
https://github.com/scikit-learn/scikit-learn/issues/5523#issuecomment-252301608 ,
ou couper le fil
https://github.com/notifications/unsubscribe-auth/AAEz65fBsMwqmkDq3DEjMSUC-W-nfn9zks5qxnZxgaJpZM4GThGc
.

Nous allons étudier cela, merci !

Quelqu'un peut-il fournir une mise à jour générale sur l'état du monde par rapport à ce problème ?

Le support des pandas DataFrame sera-t-il toujours une chose YMMV ?
Des conseils sur ce qui est/n'est pas considéré comme sûr pour une utilisation avec un panda DataFrame au lieu d'un simple ndarray seraient utiles. Peut-être quelque chose du genre de ce qui suit (TABLE D'EXEMPLE COMPOSÉ) :

module/catégorie|peut consommer en toute sécurité des pandas DataFrame
--|--
sklearn.pipeline|SÉCURISÉ
sklearn.feature_selection|SÉCURISÉ
régresseurs|YMMV
sklearn.feature_extraction|NON SÉCURISÉ, aucun plan à mettre en œuvre
etc|...

Pour le moment, je ne suis pas sûr d'une approche autre que "essayez-la et voyez si elle lève des exceptions".

Nous avons testé une poignée d'exemples codés à la main qui semblent très bien fonctionner en acceptant un DataFrame pandas, mais nous ne pouvons pas nous empêcher de penser que cela cessera inévitablement de fonctionner correctement lorsque nous déciderons que nous devons effectuer un échange de composants de pipeline apparemment trivial... à ce moment-là, tout s'effondre comme un château de cartes dans une trace de pile cryptique.

Mon processus de réflexion initial était de créer un objet de pipeline de remplacement qui peut consommer un pandas DataFrame que des wrappers générés automatiquement pour les composants scikit-learn standard pour convertir les objets d'entrée/sortie DataFrame en numpy ndarray objets si nécessaire. De cette façon, je peux écrire mes propres sélecteurs/transformateurs personnalisés afin de pouvoir utiliser les primitives DataFrame des pandas, mais cela semble un peu lourd. Surtout si nous sommes sur le point d'avoir un support "officiel" pour eux.

J'ai suivi quelques relations publiques différentes, mais il est difficile d'avoir une idée de celles qui sont abandonnées et/ou qui reflètent la pensée actuelle :
Exemple:

6425 (référencé octobre 2016 ci-dessus dans ce fil)

9012 (chevauchements évidents avec sklearn-pandas, mais annoté comme expérimental ?)

3886 (remplacé par #9012 ?)

Cela dépend essentiellement de ce que vous entendez par "Peut consommer en toute sécurité des pandas DataFrame". Si vous voulez dire un DataFrame contenant uniquement des nombres flottants, nous garantissons que tout fonctionnera. S'il y a ne serait-ce qu'une seule chaîne quelque part, rien ne fonctionnera.

Je pense que tout estimateur scikit-learn renvoyant une base de données pour toute opération non triviale (ou peut-être même triviale) est quelque chose qui pourrait ne jamais se produire (bien qu'il le souhaite).

9012 arrivera et deviendra stable, le PR est une première itération (ou 10ème itération, si vous comptez les non fusionnés ;)

6425 est susceptible de se produire, bien que cela ne soit pas entièrement lié aux pandas.

3886 est bien remplacé par #9012

La fonctionnalité #6425 est actuellement implémentée (pour certains transformateurs et
extensible à d'autres) via singledispatch dans
https://codecov.io/gh/TeamHG-Memex/eli5 pour ce que ça vaut.

Le 21 juin 2017 à 13h25, Andreas Mueller [email protected] a écrit :

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

arrivera et deviendra stable, le PR est une première itération.

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

susceptible de se produire, bien que cela ne soit pas entièrement lié aux pandas.

3886 https://github.com/scikit-learn/scikit-learn/pull/3886 est en effet

remplacé par #9012
https://github.com/scikit-learn/scikit-learn/pull/9012

-
Vous recevez ceci parce que vous avez été mentionné.
Répondez directement à cet e-mail, consultez-le sur GitHub
https://github.com/scikit-learn/scikit-learn/issues/5523#issuecomment-309952467 ,
ou couper le fil
https://github.com/notifications/unsubscribe-auth/AAEz61lgGBW1AoukPm_87elBjF2NGOUwks5sGI0-gaJpZM4GThGc
.

Oh et quand je dis " Si vous voulez dire un DataFrame contenant uniquement des nombres flottants, nous garantissons que tout fonctionnera. " Je veux dire avec l'indexation de colonne basée sur l'emplacement. les colonnes de l'ensemble d'apprentissage et de test sont supposées être les mêmes par position.

Cela dépend essentiellement de ce que vous entendez par "Peut consommer en toute sécurité des pandas DataFrame". Si vous voulez dire un DataFrame contenant uniquement des nombres flottants, nous garantissons que tout fonctionnera. S'il y a ne serait-ce qu'une seule chaîne quelque part, rien ne fonctionnera.

Je pense que c'est assez bon pour nous.

Nous utilisons un pipeline de composants personnalisés (enveloppes minces autour d'outils existants qui ne sont pas compatibles avec les pipelines) pour convertir des types mixtes (chaînes, flottants et entiers) en flottants via l'encodage/la mise à l'échelle avant d'atteindre les composants scikit-learn tels que les sélecteurs ou les modèles.

Tous mes transformateurs renvoient DataFrame s lorsqu'ils reçoivent DataFrame s.
Lorsque j'entre un DataFrame 300 colonnes dans un Pipeline et que je reçois un ndarray 500 colonnes, je ne peux pas en tirer grand-chose, par exemple, feature_selection , car je n'ai plus les noms de colonnes. Si, disons, mutual_info_classif me dit que seules les colonnes 30 et 75 sont importantes, je n'arrive pas à comprendre comment simplifier mon Pipeline pour la production.
Il est donc essentiel pour mon cas d'utilisation de conserver mes données dans un DataFrame .
Merci.

@sam-s Je suis tout à fait d'accord. À "court" terme, cela sera traité par https://github.com/scikit-learn/scikit-learn/pull/13307 et https://github.com/scikit-learn/enhancement_proposals/pull/18

Vous n'obtiendrez pas de cadre de données pandas, mais vous obtiendrez le nom de la colonne pour en créer un.

Pouvez-vous donner un exemple plus concret, s'il vous plaît? Parce que si tous les transformateurs renvoient des DataFrames, les choses devraient fonctionner (ou fonctionner plus facilement que les propositions ci-dessus).

Légère mise à jour via https://github.com/pandas-dev/pandas/issues/27211
ce qui met un frein à mes espoirs. Il semble que nous ne pouvons pas faire confiance à un aller-retour sans copie, et donc l'emballage et le déballage dans des pandas entraîneront des coûts substantiels.

Petite mise à jour via pandas-dev/pandas#27211 qui met un frein à mes espoirs. Il semble que nous ne pouvons pas faire confiance à un aller-retour sans copie, et donc l'emballage et le déballage dans des pandas entraîneront des coûts substantiels.

ouais, mais je suppose qu'une fois que nous couvrirons la fonctionnalité et les exemples d'accessoires (les noms de lignes et les "indices" étant un peu un exemple d'accessoire), la plupart des cas d'utilisation connexes qui ont un peu besoin de pandas maintenant seraient couverts, non?

@adrinjalali Je ne sais pas ce que vous entendez par "la plupart des cas d'utilisation liés avec un peu de besoin de pandas". J'ai vu ce problème non pas principalement comme une prise en charge des pandas pour implémenter des fonctionnalités dans scikit-learn, mais pour que scikit-learn s'intègre plus facilement dans un flux de travail basé sur les pandas.

Juste par curiosité, y a-t-il un délai dans lequel la compatibilité améliorée avec Pandas devrait atterrir ? Je suis particulièrement intéressé par Pandas in -> Pandas out pour StandardScaler .

J'ai un cas d'utilisation où j'ai besoin de trames de données de pandas préservées à chaque étape d'un Pipeline . Par exemple, un pipeline avec 1) une étape de sélection de fonctionnalités filtrant les fonctionnalités en fonction des données, 2) une étape de transformation des données, 3) une autre étape de sélection de fonctionnalités pour filtrer les noms de colonnes de fonctionnalités spécifiques ou les indices d'origine, 4) la normalisation, 5) la classification.

Étape 3) Je pense que ce n'est actuellement pas possible dans sklearn, même avec une entrée de tableau numpy, car les indices de caractéristiques d'origine n'ont pas de sens lorsque les données atteignent 3) car en 1) il y avait une étape de sélection de caractéristiques. Si les cadres de données pandas étaient conservés dans le pipeline, cela fonctionnerait car je pourrais filtrer par nom de colonne dans 3).

Ai-je tort de penser qu'il n'y a actuellement aucun moyen de le faire même avec une entrée de tableau numpy?

Vous avez raison de dire que ce n'est pas pris en charge, et le soutenir ne serait pas anodin. En rapport avec votre cas d'utilisation, nous travaillons sur la transmission des noms de fonctionnalités le long du pipeline (comme vous le voyez dans les PR liés et les propositions ci-dessus). Cela devrait, espérons-le, aider avec votre cas une fois que ce sera fait. Je ne sais pas si cela vous aide, mais vous pouvez également consulter https://github.com/scikit-learn-contrib/sklearn-pandas

Vous avez raison de dire que ce n'est pas pris en charge, et le soutenir ne serait pas anodin. En rapport avec votre cas d'utilisation, nous travaillons sur la transmission des noms de fonctionnalités le long du pipeline (comme vous le voyez dans les PR liés et les propositions ci-dessus). Cela devrait, espérons-le, aider avec votre cas une fois que ce sera fait.

Merci pour la confirmation, oui, pouvoir transmettre les noms de fonctionnalités (ou d'autres propriétés de fonctionnalités) pour ajuster les méthodes et les découper correctement lors de chaque étape de sélection de fonctionnalités serait bien pour ce cas d'utilisation.

Je ne sais pas si cela vous aide, mais vous pouvez également consulter https://github.com/scikit-learn-contrib/sklearn-pandas

Plus tôt, j'ai lu leurs documents et peut-être que je ne le vois pas, mais la plupart (ou toutes) de leurs fonctionnalités sont désormais obsolètes dans scikit-learn 0.21 avec sklearn.compose.ColumnTransformer ? De plus, il ne semble pas qu'ils prennent en charge les pandas, cela ressemble à des tableaux numpy après les transformations.

(Je me demande si le support des Pandas dans la sélection des fonctionnalités ne casserait pas
beaucoup...)

En vérifiant simplement brièvement le code, il existe toutes sortes de vérifications qui se produisent arbitrairement à de nombreux endroits, en utilisant par exemple https://github.com/scikit-learn/scikit-learn/blob/939fa3cccefe708db7a81c5248db32a1d600bf8d/sklearn/utils/validation.py# L619

De plus, de nombreuses opérations utilisent l'indexation d'une manière numpy qui ne serait pas acceptée par le cadre de données des pandas.

Garder les pandas à l'intérieur/à l'extérieur serait un must pour la science des données au quotidien IMO, mais scikit-learn semble être conçu d'une manière qui rendrait sa mise en œuvre difficile.

Garder les pandas à l'intérieur/à l'extérieur serait un must pour la science des données au quotidien IMO, mais
scikit-learn semble être conçu d'une manière qui rendrait difficile d'être
mis en œuvre.

Les bons chiffres sont difficiles à implémenter sur les cadres de données des pandas. ils sont juste
pas fait pour ça, en particulier pour les opérations multivariées (numérique
opérations sur les colonnes).

L'apprentissage automatique est principalement composé de nombres multivariés.

Les bons chiffres sont difficiles à implémenter sur les cadres de données des pandas. Ils ne sont tout simplement pas destinés à cela, en particulier pour les opérations multivariées (opérations numériques sur les colonnes). L'apprentissage automatique est principalement composé de nombres multivariés.

Cette décision devrait-elle être laissée à l'utilisateur ? D'après mon expérience d'utilisation intensive de scikit-learn au cours des deux dernières années, je pense que deux fonctionnalités essentielles et importantes qui manquent et sont indispensables pour de nombreux cas d'utilisation de ML sont la prise en charge de la transmission d'échantillons et de métadonnées de fonctionnalités. La prise en charge complète des dataframes pandas est un moyen naturel et élégant de gérer certains de ces problèmes.

Ce type de fonctionnalités de base est très important pour conserver la base d'utilisateurs et attirer de nouveaux utilisateurs. Sinon, je vois des bibliothèques comme par exemple mlr3 arriver à maturité et attirer les utilisateurs loin de sklearn parce que je sais qu'elles prennent (ou prendront) pleinement en charge les trames de données et les métadonnées.

Cette décision devrait-elle être laissée à l'utilisateur ?

Eh bien, l'utilisateur n'implémente pas l'algorithme.

Sinon, je vois des bibliothèques comme par exemple mlr3 qui finissent par arriver à maturité et
attirer les utilisateurs loin de sklearn parce que je sais qu'ils le font (ou le feront)
prend entièrement en charge les trames de données et les métadonnées.

mlr3 est en R, les dataframes sont assez différentes des dataframes pandas.
Peut-être que cela facilite la mise en œuvre.

Je suis d'accord qu'une meilleure prise en charge des noms de fonctionnalités et des données hétérogènes
types est important. Nous travaillons à trouver de bonnes solutions techniques
qui n'entraînent pas de perte de performances et de code trop compliqué.

Cette décision devrait-elle être laissée à l'utilisateur ?
Eh bien, l'utilisateur n'implémente pas l'algorithme.
Sinon, je vois des bibliothèques comme par exemple mlr3 arriver à maturité et attirer les utilisateurs loin de sklearn parce que je sais qu'elles prennent (ou prendront) pleinement en charge les trames de données et les métadonnées.
mlr3 est en R, les dataframes sont assez différentes des dataframes pandas. Peut-être que cela facilite la mise en œuvre. Je conviens qu'une meilleure prise en charge des noms de fonctionnalités et des types de données hétérogènes est importante. Nous travaillons à trouver de bonnes solutions techniques qui n'entraînent pas de perte de performances et de code trop compliqué.

Je pense que votre approche consistant à vous en tenir aux tableaux numpy et à au moins prendre en charge le passage des noms de fonctionnalités ou encore mieux des métadonnées de fonctionnalités multiples fonctionnerait pour de nombreux cas d'utilisation. Pour transmettre des exemples de métadonnées d'entraînement, vous les prenez déjà en charge dans **fit_params et je sais qu'il y a un effort pour améliorer la conception. Mais j'ai mentionné dans https://github.com/scikit-learn/enhancement_proposals/pull/16 qu'il existe des cas d'utilisation où vous auriez également besoin de métadonnées d'échantillon de test transmises aux méthodes transform et ce n'est pas actuellement pris en charge .

mlr3 est en R, les dataframes sont assez différentes des dataframes pandas.

Les informaticiens dans la recherche en sciences de la vie sont généralement très à l'aise avec python et R et utilisent les deux ensemble (moi y compris). Je suis presque sûr qu'un pourcentage important de la base d'utilisateurs de scikit-learn sont des chercheurs en sciences de la vie.

Actuellement, les bibliothèques de ML matures disponibles dans R IMHO ne se rapprochent même pas de scikit-learn en termes de fourniture d'une API bien conçue et de simplification des parties utilitaires de ML (pipelines, recherche d'hyperparamètres, scoring, etc.) alors que dans R avec ces bibliothèques, vous devez le coder à peu près vous-même. Mais mlr3 je vois comme une future grande compétition pour scikit-learn car ils le conçoivent à partir de zéro dans le bon sens.

Les bons chiffres sont difficiles à implémenter sur les cadres de données des pandas. ils sont juste
pas fait pour ça, en particulier pour les opérations multivariées (numérique
opérations sur les colonnes).

Il me manque peut-être quelque chose, mais ne serait-il pas possible de déballer le DataFrame (en utilisant df.values ), de faire les calculs, puis de revenir à un nouveau DataFrame ?

C'est essentiellement ce que je fais manuellement entre les étapes, et la seule chose qui empêche l'utilisation d'un Pipeline .

Peut-être qu'il me manque quelque chose, mais ne serait-il pas possible de déballer le
DataFrame (à l'aide de df.values), effectuez les calculs, puis revenez à un nouveau
Trame de données ?

En général non : ça peut ne pas fonctionner (colonnes hétérogènes), et ça va
conduire à beaucoup de copies de mémoire.

En général non : cela peut ne pas fonctionner (colonnes hétérogènes)

Je pense que Column Transformers et autres peuvent le gérer individuellement.

cela conduira à beaucoup de copies de mémoire.

Je comprends qu'il y a des choix de conception et de mise en œuvre difficiles à faire, et c'est un argument solide.

Cependant, je ne comprends pas pourquoi vous diriez que ce n'est pas une bonne idée d'améliorer la façon dont sklearn prend en charge les métadonnées de colonne.

Permettre par exemple d'ingérer un df avec des fonctionnalités, ajouter une colonne grâce à un prédicteur, faire plus de manipulations de données, faire un autre prédire, tout ça dans un Pipeline, est quelque chose qui serait utile car cela permettrait (par exemple) une optimisation d'hyper paramètres d'une manière bien mieux intégrée et élégante.

Le faire avec ou sans pandas n'est qu'une suggestion car c'est le moyen le plus courant, le plus simple et le plus populaire de manipuler des données, et je ne vois aucun avantage à réécrire plus que ce qu'ils ont fait.

Il appartiendrait à l'utilisateur de décider de ne pas utiliser ce workflow lors de l'optimisation des performances.

Laisser le soin à l'utilisateur de décider nécessite d'expliquer clairement les
choix à l'utilisateur. La plupart des utilisateurs ne lisent pas la documentation qui
expliquer de tels choix. Beaucoup essaieraient ce qu'ils pensent qui pourrait fonctionner, puis
abandonner quand ils trouvent que c'est lent, sans se rendre compte que c'était leur choix de
daraframe qui l'a rendu ainsi.

Nous devons donc faire preuve de prudence ici. Mais nous devons continuer à résoudre
ceci comme une haute priorité.

Je pense que la meilleure solution serait de prendre en charge les trames de données pandas entrantes et sortantes pour les propriétés de l'échantillon et des caractéristiques et de les transmettre et de les découper correctement pour former et tester l'ajustement/la transformation. Cela résoudrait la plupart des cas d'utilisation tout en conservant la vitesse de la matrice de données X sous forme de tableaux numpy.

Un point important qui manque à ces arguments est que les pandas se dirigent vers une représentation en colonnes des données, de sorte que np.array(pd.DataFrame(numpy_data)) aura deux copies mémoire _garanties_. C'est pourquoi ce n'est pas aussi simple que de conserver la trame de données et d'utiliser values chaque fois que nous avons besoin de vitesse.

Un point important qui manque à ces arguments est que les pandas se dirigent vers une représentation en colonnes des données, de sorte que np.array(pd.DataFrame(numpy_data)) aura deux copies mémoire _garanties_. C'est pourquoi ce n'est pas aussi simple que de conserver la trame de données et d'utiliser values chaque fois que nous avons besoin de vitesse.

J'espère avoir été clair dans mon message précédent. Je pense que scikit-learn n'a actuellement pas besoin de prendre en charge les trames de données pandas pour les données X, conservez-les sous forme de tableaux numpy rapides. Mais ce qui résoudrait de nombreux cas d'utilisation, c'est la prise en charge complète via le cadre des cadres de données pandas pour les métadonnées, c'est-à-dire les propriétés des exemples et les propriétés des fonctionnalités. Cela ne devrait pas être un fardeau pour les performances, même pour les copies mémoire, car ces deux structures de données seront mineures par rapport à X et, en réalité, seul un sous-réglage sera effectué sur celles-ci.

Oui, ces changements aident dans de nombreux cas d'utilisation, et nous y travaillons. Mais ce problème va au-delà de cela : https://github.com/scikit-learn/scikit-learn/issues/5523#issuecomment -508807755

@hermidalc suggérez -vous que nous X être un tableau numpy et que nous attribuions les métadonnées à d'autres objets de données ?

@hermidalc suggérez -vous que nous X être un tableau numpy et que nous attribuions les métadonnées à d'autres objets de données ?

Oui, prise en charge complète des exemples de propriétés et de propriétés de fonctionnalités en tant que trames de données pandas. Des discussions sont déjà en cours sur des exemples de propriétés et de noms de fonctionnalités dans d'autres PR et problèmes, par exemple ici #9566 et #14315

J'ai lu sur ce problème et il semble qu'il y ait deux bloqueurs majeurs ici :

  1. https://github.com/pandas-dev/pandas/issues/27211
  2. Ce pandas ne gère pas les tableaux ND.

Avez-vous envisagé d'ajouter la prise en charge des xarrays à la place ? Ils n'ont pas les limitations des 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

Il existe un package appelé sklearn-xarray : https://phausamann.github.io/sklearn-xarray/content/wrappers.html qui enveloppe les estimateurs scikit pour gérer les xarrays en entrée et en sortie mais qui semble ne pas avoir été maintenu pendant années. Cependant, je me demande si les emballages sont la voie à suivre ici.

xarray est activement à l'étude. Il est en cours de prototypage et de travail ici : https://github.com/scikit-learn/scikit-learn/pull/16772 Il existe un cahier d'utilisation sur ce à quoi ressemblerait l'API dans le PR.

(J'y reviendrai après avoir terminé avec la version 0.23)

Je suis également très intéressé par cette fonctionnalité.
Cela résoudrait des problèmes infinis. C'est actuellement la solution que j'utilise.
J'ai écrit un wrapper autour du module sklearn.preprocessing , que j'ai appelé sklearn_wrapper

Donc, au lieu d'importer depuis sklearn.preprocessing j'importe depuis sklearn_wrapper .
Par exemple:

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

Ci-dessous la mise en œuvre de ce module. Essayez-le et dites-moi ce que vous en pensez

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 a fourni une autre solution par Hot-fixing sur le sklearn.pipeline.FeatureUnion

Cette page vous a été utile?
0 / 5 - 0 notes