Scikit-learn: Überdenken Sie die CategoricalEncoder-API?

Erstellt am 23. Jan. 2018  ·  63Kommentare  ·  Quelle: scikit-learn/scikit-learn

Aufgrund einiger Diskussionen, die wir hier führen, und offener Probleme, haben wir einige Zweifel, dass CategoricalEncoder (https://github.com/scikit-learn/scikit-learn/pull/9151) das Gute war Wahl des Namens (und da es noch nicht veröffentlicht ist, haben wir noch etwas Raum für Änderungen).

Also Zusammenfassung, wie es jetzt ist:

  • Der Klassenname CategoricalEncoder sagt aus, welche Art von Daten er akzeptiert (kategoriale Daten)
  • Das Schlüsselwortargument encoding gibt an, wie diese Daten kodiert werden

Derzeit haben wir bereits encoding='onehot'|'onehot-dense'|'ordinal' .

Aber was ist in folgenden Fällen zu tun:

  • Wir wollen weitere Encoding-Optionen hinzufügen (zB Binary Encoding, Mean Target Encoding, Unary Encoding, ...). Fügen wir diese weiterhin als neue Werte für den encoding Kwarg in der einen großen CategoricalEncoder Klasse hinzu?
  • Wir möchten eine Option hinzufügen, die für eine der Kodierungen spezifisch ist (z. B. für 'onehot'-Kodierung, um die erste (redundante) Spalte zu löschen, oder für 'ordinale' Kodierung basierend auf der Reihenfolge der Kategorien nach der Häufigkeit, ...). Das Problem dabei ist, dass wir dann zusätzliche Schlüsselwortargumente zu CategoricalEncoder hinzufügen müssen, die aktiv sind oder nicht, je nachdem, was Sie für encoding kwarg übergeben haben, was nicht das schönste API-Design ist.

Für das letzte Problem hatten wir dies bereits mit der Option sparse=True/False , die nur für 'onehot' und nicht für 'ordinal' relevant war und die wir mit 'onehot' und 'onehot-dense' gelöst haben. Codierungsoptionen und kein sparse Schlüsselwort. Aber auch ein solcher Ansatz skaliert nicht.

In diesem Zusammenhang gibt es einen PR zum Hinzufügen eines UnaryEncoder (https://github.com/scikit-learn/scikit-learn/pull/8652). Es gab eine diesbezügliche Diskussion über die Benennung in dieser PR, da der Name derzeit sagt, wie sie kodiert, nicht welche Art von Daten sie erhält (im aktuellen Design akzeptiert sie bereits kodierte Ganzzahlen, keine tatsächlichen kategorialen Daten. In dieser Hinsicht zu mit CategoricalEncoder konsistent sein, sollte es besser OrdinalEncoder heißen, da es ordinale Daten als Eingabe benötigt).


Welche Möglichkeiten gibt es weiter:

1) Behalten Sie die Dinge bei, wie wir sie jetzt in Master haben, und seien Sie in Ordnung, wenn Sie der einzelnen Klasse einige neue Optionen hinzufügen (eine wichtige Frage, die jetzt schwer zu beantworten ist, ist, wie viele neue Funktionen wir in Zukunft hinzufügen möchten) .
2) Wechseln Sie das Benennungsschema und haben Sie eine Reihe von "kategorialen Encodern", bei denen der Name sagt, wie es codiert (OnehotEncoder, OrdinalEncoder und später vielleicht BinaryEncoder, UnaryEncoder, ...)

Es handelt sich also um einen Kompromiss zwischen dem möglichen Aufbau der Anzahl der Klassen und der Anzahl der Schlüsselwortargumente in einer einzelnen Klasse.


Ein Problem mit dem zweiten Ansatz (und einer der Gründe, warum wir uns zuerst für CategoricalEncoder haben, noch bevor wir die mehreren Codierungsoptionen hinzugefügt haben), ist, dass es bereits ein OnehotEncoder , das hat eine andere API als die CategoricalEncoder . Und es gibt keinen wirklich guten anderen Namen, den wir für den Encoder verwenden könnten, der One-Hot-Encoding durchführt.
Ich denke jedoch, dass wir den Namen mit einigen vorübergehenden hässlichen Hacks wiederverwenden könnten, wenn wir damit einverstanden sind, die aktuellen Attribute abzulehnen (und ich denke, wir stimmen zu, dass dies nicht die nützlichsten Attribute sind). Die Idee wäre, dass Sie, wenn Sie die Klasse mit Zeichenfolgendaten anpassen, das neue Verhalten erhalten, und wenn Sie die Klasse mit Ganzzahldaten anpassen, erhalten Sie eine veraltete Warnung, die angibt, dass sich das Standardverhalten ändert (und angibt, welches Schlüsselwort angegeben werden soll) Warnung loswerden).

cc @jnothman @amueller @GaelVaroquaux @rth

Hilfreichster Kommentar

Die Idee, CategoricalEncoder zurückzusetzen, macht mich ziemlich traurig, aber ich denke
Sie haben Recht, dass zukünftige Benutzer von Option 2 weniger verwirrt wären
Sorge ist, dass wir versucht haben, dies als Änderung von OHE für a
lange Zeit und es ist nie geflogen. Vielleicht wäre es gut, dies zu versuchen
Änderungen am OneHotEncoder-Docstring gemäß dem Vorschlag
ändern, damit wir sehen können, ob es vernünftig aussieht.

Alle 63 Kommentare

Danke für die Zusammenfassung @jorisvandenbossche. Ich denke, ich befürworte Option 2: Wiederverwendung der OneHotEncoder Klasse, veraltete Attribute und füge einen Konstruktorparameter hinzu, um das Verhalten mit einer zukünftigen Warnung auszuwählen, die besagt, dass sich das Standardverhalten ändert, aber es einfach macht, es zum Schweigen zu bringen diese Warnung, indem Sie einfach einen Wert für diese Option übergeben.

Die Idee, CategoricalEncoder zurückzusetzen, macht mich ziemlich traurig, aber ich denke
Sie haben Recht, dass zukünftige Benutzer von Option 2 weniger verwirrt wären
Sorge ist, dass wir versucht haben, dies als Änderung von OHE für a
lange Zeit und es ist nie geflogen. Vielleicht wäre es gut, dies zu versuchen
Änderungen am OneHotEncoder-Docstring gemäß dem Vorschlag
ändern, damit wir sehen können, ob es vernünftig aussieht.

+1 zu dem, was Joel gesagt hat

⁣Gesendet von meinem Telefon. Bitte verzeihen Sie Tippfehler und Kürze.​

Am 23. Januar 2018, 12:28, um 12:28 Uhr schrieb Joel Nothman [email protected] :

Die Idee, CategoricalEncoder zurückzusetzen, macht mich ziemlich traurig, aber ich
denken
Sie haben Recht, dass zukünftige Benutzer von Option 2 weniger verwirrt wären
hauptsächlich
Besorgniserregend ist, dass wir versucht haben, dies als Änderung von OHE für
ein
lange Zeit und es ist nie geflogen. Vielleicht wäre es gut, dies zu versuchen
Änderungen am OneHotEncoder-Docstring gemäß dem Vorschlag
ändern, damit wir sehen können, ob es vernünftig aussieht.

--
Sie erhalten dies, weil Sie erwähnt wurden.
Antworten Sie direkt auf diese E-Mail oder zeigen Sie sie auf GitHub an:
https://github.com/scikit-learn/scikit-learn/issues/10521#issuecomment -359761818

Die Idee, CategoricalEncoder zurückzusetzen, macht mich ziemlich traurig

Um es klar zu sagen, es wäre kein Revert, es wäre ein Refactor / Rename, der alle Funktionen beibehält!
Aber auch der Name "CategoricalEncoder" gefällt mir, das wäre in der Tat traurig.

Trotzdem werde ich schnell versuchen, die Änderungen vorzunehmen, um eine Vorstellung davon zu haben, wie es möglich ist, dies in OnehotEncoder zu integrieren.

OK, ich habe eine PR mit einem Proof of Concept eröffnet: https://github.com/scikit-learn/scikit-learn/pull/10523.
Es ist noch nicht vollständig (keine Veraltungswarnungen und neue Attribute werden noch nicht im alten Verhalten berechnet).

Die wichtigste API-Frage betrifft das Format der Eingabedaten.
Zusammenfassend lässt sich sagen, dass wir derzeit die kategorialen Daten auf zwei verschiedene Arten verarbeiten :

1) Als tatsächliche, noch nicht codierte (Integer oder String), kategoriale Daten (wie es in CategoricalEncoder ) -> Kategorien aus den eindeutigen Werten in den Trainingsdaten ableiten
2) Als Integer, bereits codierte Daten (wie es im aktuellen OneHotEncoder ) -> Kategorien aus dem Maximalwert in den Trainingsdaten ableiten

Die Frage ist: Finden wir beide Fälle unterstützenswert? Behalten wir also im potenziell zusammengeführten OneHotEncoder die Möglichkeit, beides zu tun, oder verwerfen wir die Möglichkeit, ordinale Eingaben zu verarbeiten, vollständig und entfernen sie dann?

Wenn Sie beides verarbeiten möchten, können wir ein boolesches Schlüsselwort hinzufügen, um den Eingabedatentyp anzugeben (im Moment verwende ich encoded_input=False/True , aber andere Ideen sind ordinal_input , ...)

Für die Deprecation-Periode müssen wir sowieso beides unterstützen und auch ein Schlüsselwort zur Auswahl des Verhaltens einführen (um die Warnung stummschalten und das neue Verhalten auswählen zu können).
Im Prinzip könnten wir also das Keyword hinterher einfach behalten.

Da wir beides behandeln möchten, ein Überblick darüber, wie OneHotEncoder funktionieren würde:

  • vorerst encoded_input=None , und wir leiten den Standard basierend auf den Daten ab
  • if int-like data (vorher von OneHotEncoder behandelt) encoded_input wird intern auf True gesetzt und eine veraltete Warnung ausgegeben. Wenn der Benutzer das aktuelle Verhalten beibehalten möchte, kann er es manuell als OneHotEncoder(encoded_input=True) angeben, um die Warnung stumm zu schalten.
  • wenn die Eingabe nicht int-like ist, setzen wir encoded_input intern auf False und verwenden ohne Vorwarnung das neue Verhalten (= das aktuelle CategoricalEncoder-Verhalten)
  • in Zukunft ändern wir den Standardwert von encoded_input von None auf False (standardmäßig das neue Verhalten, auch für int-ähnliche Daten)

Ich bin mir immer noch nicht sicher, was Sie vorschlagen, ist der praktische Unterschied aufgrund der Ableitung von Kategorien aus dem Höchstwert.

@jnothman Ich nehme an, Sie erkennen an, dass es in der Praxis einen Unterschied geben kann ? (die Ausgabe, die Sie erhalten, hängt von den Daten ab, die Sie haben)

Aber ob dieser Unterschied in der Praxis wichtig ist, weiß ich nicht. Da würde ich gerne Feedback sehen. Ob irgendjemand diese "max-Wert"-basierte Methode wirklich will , oder ob wir (in Zukunft, nach der Einstellung) nur noch die "eindeutige Werte"-basierte Methode haben wollen.

Ich denke, ich persönlich würde diese max-value-basierte Methode nie brauchen, aber der OneHotEncoder ist seit vielen Jahren so (aus gutem Grund oder nicht?).

Die tatsächliche Einstellung der Maximalwert-basierten Kategorisierung würde die Implementierung (nach der Einstellung) sicherlich einfacher machen.
Und wenn wir uns für diese Route entscheiden, stimme ich zu, dass die Option eher legacy_mode=True/False als encoded_input / ordinal_input

Erinnern Sie mich daran, was der tatsächliche Unterschied in der Ausgabe ist, wenn n_values='auto',
bitte? Ich hatte gedacht, die active_features_-Sache macht sie im Grunde
identisch, aber ich vergesse wahrscheinlich etwas.

Aha, das klärt unser Missverständnis :-)
Ich habe falsch verstanden, wie der aktuelle OneHotEncoder tatsächlich funktioniert. Angenommen, Sie haben ein Feature mit den Werten [2, 3, 5, 2]. Ich dachte, der aktuelle OneHotEncoder hätte die Kategorien [0, 1, 2, 3, 4, 5] (während der aktuelle CategoricalEncoder die Kategorien [2, 3, 5] haben würde). Aber Sie haben Recht, dass active_features_ auch nur [2, 3, 5] ist, was sie im Wesentlichen mit dem Standardwert von n_values='auto' .

Es ist also nur der Fall, wenn Sie eine ganze Zahl an n_values (wie n_values=6 für Kategorien=[0, 1, 2, 3, 4, 5] im obigen Fall), um die Anzahl der Kategorien, bei denen es sich tatsächlich um eine API-Änderung handelt (veraltet / entfernt).
Und das kann der Benutzer leicht durch categories=range(6) ersetzen

Entschuldigung für die Verwirrung.
In diesem Licht denke ich, dass wir die Option legacy_mode nicht einmal brauchen. Wir können n_values=6 einfach intern in categories=range(6) und eine Warnung dafür ausgeben (aber müssen dies mit den tatsächlichen Tests überprüfen).

Der andere Unterschied ist der Umgang mit unsichtbaren Kategorien. Mit dem aktuellen Verhalten des OneHotEncoder, wenn die unsichtbaren Werte innerhalb des Bereichs (0, max) liegen, wird kein Fehler ausgegeben, selbst wenn handle_unknow='error' (Standard) ist. Aber auch das kann separat gelöst werden, indem in einem solchen Fall eine Warnung ausgegeben wird, dass der Benutzer handle_unknown='ignore' manuell setzen sollte, um das bestehende Verhalten beizubehalten.

Das einzige Feature, das wir verlieren würden, ist die Unterscheidung zwischen unbekannten Kategorien, die innerhalb des Bereichs (0, max) liegen (vom aktuellen OneHotEncoder nicht als 'unbekannt' angesehen) und solchen, die größer sind (> max, diese werden derzeit bereits berücksichtigt .) als unbekannt vom OneHotEncoder).

nein, sowas haben wir schon mal probiert und es ist einfach zu
pingelig. es sei denn, es gibt triftige Gründe, das aktuelle Verhalten beizubehalten, wir
sollte nur einen Legacy_mode haben, um uns langsam in die Zukunft zu bringen.

nein, so etwas haben wir schon mal probiert und es ist einfach zu pingelig.

Können Sie klären, auf welchen Aspekt sich dieses „Nein“ bezieht?
An der Tatsache, dass ich denke, dass ein legacy_mode nicht benötigt wird?

ja, zu der Idee, dass man einfach etwas machen kann, das beides rückwärts ist
kompatibel und was wir für die Zukunft wollen

Ja, zu der Idee, dass man einfach etwas machen kann, das sowohl abwärtskompatibel ist als auch das, was wir in Zukunft wollen

Das war nicht das, was ich vorschlagen wollte. Ich wollte klarstellen, dass es möglich ist, kein legacy_mode Schlüsselwort zu haben, nicht indem man es auf magische Weise rückwärtskompatiert und das, was wir in Zukunft wollen, sondern indem man das Verhalten der vorhandenen Schlüsselwörter ablehnt.

Um konkret zu sein: Ein nicht standardmäßiger Wert von n_values kann veraltet sein und muss durch eine categories Spezifikation ersetzt werden. handle_unknow im Fall von Integer-Daten sollte explizit vom Benutzer festgelegt werden, um entweder vollständiges Ignorieren oder vollständiges Fehlern statt des aktuellen Mixes zu wählen (und andernfalls wird eine Veraltungswarnung ausgegeben).

also wenn ich .fit([[5]]).transform([[4]]) tue, für welche Werte von n_values,
Kategorien und handle_umknown wird das einen Fehler auslösen?

Am 25. Januar 2018 um 9:32 Uhr, "Joris Van den Bossche" [email protected]
schrieb:

ja, zu der Idee, dass man einfach etwas machen kann, das beides rückwärts ist
kompatibel und was wir für die Zukunft wollen

Das war nicht das, was ich vorschlagen wollte. Ich wollte klarstellen, dass ich es denke
Es ist möglich, kein Legacy_mode-Schlüsselwort zu haben, nicht indem man es magisch hat
sowohl rückwärtskompatiert als auch das, was wir in Zukunft wollen, aber indem wir es ablehnen
das Verhalten der vorhandenen Keywords.

Um konkret zu sein: Ein Nicht-Standardwert von n_values ​​kann veraltet sein und
muss durch Kategorienangabe ersetzt werden. handle_unknow im Fall von
Ganzzahldaten sollten vom Benutzer explizit festgelegt werden, um entweder full
Ignorieren oder vollständiger Fehler anstelle der aktuellen Mischung (und ansonsten veraltet)
Warnung wird ausgelöst).


Sie erhalten dies, weil Sie erwähnt wurden.
Antworten Sie direkt auf diese E-Mail und zeigen Sie sie auf GitHub an
https://github.com/scikit-learn/scikit-learn/issues/10521#issuecomment-360296569 ,
oder den Thread stumm schalten
https://github.com/notifications/unsubscribe-auth/AAEz6-DrQWep22_gs-hg9cC0u19B1_PSks5tN6-HgaJpZM4RpUE8
.

Können wir es einfach so machen, dass während der Einstellung Kategorien festgelegt werden müssen?
explizit, und der Legacy-Modus mit Warnungen ist ansonsten in Kraft? Ist dass
was schlagen Sie vor?

Können wir es einfach so machen, dass Kategorien während der Einstellung explizit festgelegt werden müssen und der Legacy-Modus mit Warnungen ansonsten wirksam ist? Ist es das, was Sie vorschlagen?

Ja, es könnte immer noch ein fehlender Fall sein, aber ich denke, dies ist möglich (wird nächste Woche durch tatsächliches Codieren überprüfen).

Die verschiedenen 'Legacy'-Fälle:

  • n_values='auto' (die Standardeinstellung)

    • handle_unknown='ignore' -> gut, keine Verhaltensänderung

    • handle_unknown='error' -> Problem, Werte im Bereich werden weiterhin ignoriert, Werte über dem Bereich error



      • Mögliche Lösung:





        • passt, wenn der Bereich fortlaufend ist => in Ordnung, keine Verhaltensänderung (für alle Leute, die jetzt LabelEncoder damit kombiniert haben, was meiner Meinung nach ein typischer Anwendungsfall ist)



        • Wenn dies nicht der Fall ist: Warnung vor Veraltung ausgeben, dass Kategorien explizit festgelegt werden müssen, um dieses Verhalten beizubehalten (und intern den Legacy-Modus verwenden).






  • n_values=Wert

    • dies kann intern in category=[range(value)] übersetzt werden und eine veraltete Warnung ausgeben, dass der Benutzer dies in Zukunft selbst tun sollte

    • in diesem Fall funktionieren handle_unknown='error' / 'ignore' wie erwartet

Die Veraltungswarnung im Fall von n_values='auto' wird nur in fit und nicht beim Bau (was nicht wirklich ideal ist), aber es ist nur angemessen, dass wir wissen, dass der Benutzer sie übergibt numerische Daten und keine Zeichenfolgendaten.

Wir geben normalerweise keine Warnungen heraus, bis es auf jeden Fall passt, also mach dir keine Sorgen
das.

diese Strategie klingt meistens gut.

Ich bin mir nicht sicher, ob wir nach Strings in den Daten suchen sollten,
obwohl. Sie wollen es grundsätzlich so haben: Der Legacy-Modus ist aktiv, wenn die Kategorien aktiviert sind
nicht gesetzt und wenn die Daten alle Ganzzahlen sind?

Eine Frage: Wenn Kategorien und n_values-Parameter ihre Standardwerte sind, tun Sie
Wir veröffentlichen Kategorien_? Wenn n_values ​​explizit gesetzt ist, veröffentlichen wir?
Kategorien_?

Am 29.01.2018 10:00 Uhr, "Joris Van den Bossche" [email protected]
schrieb:

Können wir es einfach so machen, dass während der Einstellung Kategorien festgelegt werden müssen?
explizit, und der Legacy-Modus mit Warnungen ist ansonsten in Kraft? Ist dass
was schlagen Sie vor?

Ja, es könnte immer noch ein fehlender Fall sein, aber ich denke, das ist möglich (wird
Überprüfen Sie, indem Sie es nächste Woche tatsächlich codieren).

Die verschiedenen 'Legacy'-Fälle:

  • n_values='auto' (die Standardeinstellung)

    • handle_unknown='ignore' -> gut, keine Verhaltensänderung

    • handle_unknown='error' -> Problem, Werte im Bereich sind noch

      ignoriert, Werte über dem Bereichsfehler



      • Mögliche Lösung:





        • in fit, wenn der Bereich fortlaufend ist => fein, keine Änderung in



          Verhalten (für alle Leute, die jetzt LabelEncoder damit kombiniert haben, das ist



          ein typischer Anwendungsfall, denke ich)



        • wenn dies nicht der Fall ist: Warnung vor Veraltung ausgeben, dass



          sie müssen explizit Kategorien festlegen, um dieses Verhalten beizubehalten (und



          intern den Legacy-Modus verwenden)





      • n_values=Wert



    • dies kann intern in category=[range(value)] übersetzt werden,

      und geben Sie eine Veraltungswarnung aus, dass der Benutzer dies selbst tun sollte

      Zukunft

    • In diesem Fall funktioniert handle_unknown='error' / 'ignore' wie erwartet

Die Veraltungswarnung im Fall von n_values='auto' wird nur in . ausgegeben
Passform und nicht auf Konstruktion (was nicht wirklich ideal ist), aber es ist nur
in Anpassung, dass wir wissen, dass der Benutzer numerische Daten und keine Zeichenfolge übergibt
Daten.


Sie erhalten dies, weil Sie erwähnt wurden.
Antworten Sie direkt auf diese E-Mail und zeigen Sie sie auf GitHub an
https://github.com/scikit-learn/scikit-learn/issues/10521#issuecomment-361104495 ,
oder den Thread stumm schalten
https://github.com/notifications/unsubscribe-auth/AAEz6x8xnyZXBLij-DCC45JyYNf8pA5kks5tPPwXgaJpZM4RpUE8
.

Sie wollen es grundsätzlich so haben: Der Legacy-Modus ist aktiv, wenn keine Kategorien festgelegt sind und die Daten alle Ganzzahlen sind?

Ja in der Tat (in der Praxis wird es mehr oder weniger dasselbe sein)

Eine Frage: Wenn die Parameter Kategorien und n_values ​​ihre Standardwerte sind, veröffentlichen wir dann Kategorien_? Wenn n_values ​​explizit festgelegt ist, veröffentlichen wir dann Kategorien_?

Ich persönlich würde schon so viel wie möglich die Attribute der neuen Schnittstelle bereitstellen, auch im Legacy-Modus. Also würde ich in beiden Fällen categories_ berechnen (auch wenn es etwas mehr Arbeit wäre)


Also habe ich versucht, die obige Logik in Code einzufügen (wird einige Updates an den PR übertragen), und ich habe noch eine Frage für den Fall von Integer-Daten, wenn n_values oder categories nicht festgelegt ist ( typischer Fall für 'legacy_mode'). Das Problem liegt darin, dass, wenn die abgeleiteten Kategorien einfach ein fortlaufender Bereich (0, 1, 2, 3, ... max) sind, kein Unterschied zwischen dem neuen und dem alten (alten) Verhalten besteht, und wir nicht unbedingt eine Veraltungswarnung ausgeben müssen.
Einige Möglichkeiten in diesem speziellen Fall:

1) Erkennen Sie diesen Fall (dass die abgeleiteten Kategorien ein fortlaufender Bereich sind) und geben Sie in diesem Fall keine Warnung aus.
- Dies ist (mit etwas zusätzlichem Code-Komplexität) zu erkennen, da wir sowieso schon fit sind
- Ich denke, dies wird ein häufiger Fall sein, wenn OneHotEncoder mit Integer-Daten verwendet wird, und ein Fall, in dem sich der Benutzer tatsächlich nicht um unser Refactoring kümmern muss, daher wäre es schön, ihn / sie nicht mit einer Warnung zu belästigen
2) Geben Sie immer eine Warnung aus und geben Sie in der Warnmeldung an, was in einem solchen Fall zu tun ist (zusätzlich zu einer Erklärung, was zu tun ist, wenn Sie keinen fortlaufenden Bereich haben):
- Wenn sie wissen, dass sie nur aufeinanderfolgende Bereiche als Kategorien haben, möchten sie die Warnung ignorieren, damit wir der Warnmeldung eine Erklärung hinzufügen können (fügen Sie ein Codebeispiel mit Filterwarnungen hinzu, die sie kopieren und einfügen können).
- Ein potenzieller Vorteil davon ist, dass wir der Warnmeldung auch hinzufügen können, dass sie, wenn sie den LabelEncoder zum Erstellen der Ganzzahlen verwendet haben, jetzt direkt OneHotEncoder verwenden können (ich denke, dies ist derzeit ein typisches Nutzungsmuster). Auf diese Weise verschwindet auch die Warnung
3) Geben Sie immer eine Warnung aus, aber geben Sie ein Schlüsselwort an, um sie zum Schweigen zu bringen (z. B. legacy_mode=False )
- Wenn uns der Rat, eine filterwarnings Anweisung zu verwenden (siehe Punkt 2) zu umständlich ist, könnten wir auch ein Schlüsselwort hinzufügen, um das gleiche Ergebnis zu erhalten
- Der Nachteil davon ist die Einführung eines Schlüsselworts, das in einigen Releases nicht mehr benötigt wird, wenn die veralteten Versionen bereinigt werden.

Ich persönlich befürworte Option 1 oder 2. Die Verwendung des LabelEncoder vor OneHotEncoder scheint ein typisches Muster zu sein (aus einer schnellen Github-Suche), und in diesem Fall hat man immer aufeinanderfolgende Bereiche, und es wird nie eine Änderung im Verhalten mit geben die neue Implementierung, also sollten wir nicht davor warnen. Auf der anderen Seite, wenn wir warnen, können wir sie darauf hinweisen, dass sie dies nicht mehr tun müssen, wenn sie LabelEncoder verwendet haben. Was schön wäre, diesen Rat explizit zu geben.
Die Frage ist, wie häufig Benutzer solche aufeinanderfolgenden Ganzzahlen als Kategorien haben, ohne im vorherigen Schritt LabelEncoder verwendet zu haben.

Hmm, ein Fall, den ich vergessen habe, ist, wenn Sie ganzzahlige abgeleitete Kategorien haben, die nicht aufeinanderfolgend sind (sagen wir [1,3,5]), aber Sie möchten das neue Verhalten und nicht das alte Verhalten (in diesem Fall können Sie die Warnung also nicht einfach ignorieren , da dies ungesehene Werte im Transformationsschritt anders behandeln würde, dh Werte zwischen dem Bereich (zB 2) führen nicht zu einem Fehler).
Falls wir das Schlüsselwort legacy_mode=False nicht bereitstellen, besteht die einzige Möglichkeit, das neue Verhalten zu erhalten, darin, categories=[1,3,5] manuell zu übergeben, was ein wenig umständlich sein kann. Das könnte ein Grund sein, Option 3 zu bevorzugen und meinen Einwand gegen die Einführung eines temporären Keywords legacy_mode=False aufzugeben (aber auch nicht ganz sicher, ob es sich lohnt, da dies der einzige Fall* wäre, in dem ein solches Keyword tatsächlich vorhanden ist erforderlich)

* nur dieser Fall = ganzzahlige Daten mit abgeleiteten Kategorien, die nicht aufeinanderfolgende Bereiche sind und bei denen Sie die Kategorien nicht manuell festlegen können / möchten oder handle_unknown auf Ignorieren setzen.

Sorry für den langen Text, aber es ist ziemlich komplex :)

Wir sprechen nur über den Fall, in dem n_values ​​nicht gesetzt ist, oder?

Mir geht es gut mit 1., und teurer wäre es auch nicht, da auto
muss bereits den Etikettensatz untersuchen. könnte ich auch akzeptieren, denn
Einfachheit, eine Variante von 3. das war nur "OneHotEncoder läuft in Legacy"
Modus. Set category='auto' für etwas anderes Verhalten ohne a
Warnung."

Wir sprechen nur über den Fall, in dem n_values ​​nicht gesetzt ist, oder?

Ja (der andere Fall kann leicht in den entsprechenden categories Wert übersetzt werden, mit einer netten Veraltungswarnung und ohne Unterschied im neuen und alten Verhalten)

eine Variante von 3. das war nur "OneHotEncoder läuft im Legacy-Modus. Set category='auto' für etwas anderes Verhalten ohne Warnung."

Ah, das klingt nach einer guten Idee! (unabhängig davon, ob der Fall der aufeinanderfolgenden Kategorien erkannt wird oder nicht). Also setzen wir im Code den Standardwert von categories auf None (ohne die Semantik seines Standardwerts zu ändern), damit wir wissen, ob der Benutzer es explizit gesetzt hat, und auf diese Weise ist es eine gute Möglichkeit, legacy_mode=False anzugeben.

Ja, aber nur, wenn wir jedes Mal warnen wollen, wenn jemand es benutzt, ohne zu passieren
Kategorien. Es ist der billige Implementierungsansatz, aber es könnte sein
für die User unnötig ausführlich, weshalb ich 1 bevorzugen würde, wenn es so wäre
kann einfach gemacht werden.

Was ist das für eine frische Hölle :-/

ODER wir könnten den neuen DummyEncoder ;) (obwohl das ein bisschen im Widerspruch zum DummyClassifier steht)

@amueller Lies nicht alles oben!
Ich wollte nur eine schöne Zusammenfassung für neue Leser der Ausgabe erstellen. Die obige Diskussion ist zu kompliziert (auch weil ich das aktuelle komplexe Verhalten von OneHotEncoder noch nicht ganz verstanden habe ... :-))

ODER wir könnten den neuen DummyEncoder nennen ;)

Ich denke, @GaelVaroquaux war dagegen, weil "one-hot" in mehr Bereichen bekannt ist (und wir verwenden 'Dummy' bereits für andere Dinge in scikit-learn ...)

Dies zu wiederholen, um die Benennung konsistent zu machen, lohnt sich imho nicht. Wir sind nirgendwo einheitlich in der Namensgebung. Können Sie die Diskussionen zusammenfassen, die dazu geführt haben?

Ich denke, "Dummy" verwenden Statistiker und Pandas.

Der Top-Beitrag ist immer noch genau und lesenswert und fasst die Gründe für das Nichtbeibehalten von CategoricalEncoder zusammen (was nicht bedeutet, dass wir OneHotEncoder anstelle von z. B. DummyEncoder verwenden müssen, das ist eine separate Frage)

Ich habe den obersten Beitrag gelesen. Darauf habe ich mich bezogen, als ich sagte, dass es sich nicht lohnt, dies aus Konsistenzgründen zu wiederholen.

Themen, die geöffnet werden

Kannst du das erklären?

"Dies aus Konsistenzgründen zu wiederholen lohnt sich nicht"

Zeigen Sie konsequent auf das Benennungsschema "was es akzeptiert" vs "was es tut" ? Wenn ja, war das nur ein kleiner Grund. Für mich ist es hauptsächlich eine Frage der Skalierbarkeit, um mehr Funktionen zu einer einzigen Klasse hinzuzufügen.

Themen, die geöffnet werden

Wir hatten das Problem, wie man mit fehlenden Werten umgeht (https://github.com/scikit-learn/scikit-learn/issues/10465), und dafür könnten Sie ein anderes Verhalten für Ordinal- und One-Hot-Codierung wünschen (oder nicht) alle Optionen gelten für beide, ..). Wir haben auch bereits das vorhandene handle_unknown das nur für das One-Hot-Encoding und nicht für die Ordinalcodierung relevant ist. Und es gab https://github.com/scikit-learn/scikit-learn/issues/10518 über Feature-Gewichtung für Onehot-Encoding, aber auch nicht relevant für Ordinal (dieses Problem war am Ende kein Problem, wie Sie es tun können) die Gewichtung mit dem ColumnTransformer transformator_weights-Argument). Und wir haben auch die Feature-Anfrage, etwas wie drop_first für One-Hot hinzuzufügen, was wiederum für die Ordinalcodierung nicht relevant ist.

Ich sehe nicht, wie die vorgeschlagene Änderung bei den fehlenden Werten so viel helfen würde. Und inkompatible Optionen zu haben, ist etwas, das bei scikit-learn oft vorkommt. Nicht ideal, aber imho auch keine große Sache.

Ich sehe nicht, wie die vorgeschlagene Änderung bei den fehlenden Werten so viel helfen würde.

Es hilft nicht als solches , aber es macht es weniger komplex, spezifische Optionen zu haben, die speziell auf die verschiedenen Codierungstypen zugeschnitten sind.

Und inkompatible Optionen zu haben, ist etwas, das bei scikit-learn oft vorkommt. Nicht ideal, aber imho auch keine große Sache.

Derzeit ist es sicherlich noch OK, es gibt nicht allzu viele inkompatible Optionen (aber auch teilweise, weil ich sparse=True/False in die Option encoding verschoben habe). Die Frage ist jedoch, inwieweit wir die Encoding-Funktionalität in scikit-learn in Zukunft erweitern wollen. Was jetzt natürlich schwer zu beantworten ist.
Wir haben bereits eine PR für 'unäre Kodierung'. Sollte dies nicht eher zu CategoricalEncoder hinzugefügt werden, anstatt eine neue Klasse UnaryEncoder hinzuzufügen? Und was ist, wenn jemand eine 'binäre Kodierung' hinzufügen möchte? Oder ein '(mittlerer) Zielencoder'?

Der "mittlere Zielencoder" ist CountTransformer , dafür gibt es eine PR ;)

Hast du dazu einen Link? Die Suche nach "CountTransformer" liefert keine Ergebnisse

Entschuldigung, CountFeaturizer #9614

Es ist sicherlich verwandt, aber nicht gerade eine durchschnittliche Zielcodierung. Außerdem werden Spalten hinzugefügt, nicht ersetzt, daher funktioniert es für kategoriale Stringdaten noch nicht sofort (aber das ist mehr Feedback zu diesem PR, das hier nicht erörtert wird).

Warum ist es nicht die Zielcodierung? Aber ja, lass uns hier nicht zu viel ablenken ;)

Als Zusammenfassung der eigentlichen Fragen, die wir beantworten müssen (in dieser Reihenfolge!):

  1. Behalten wir das aktuelle CategoricalEncoder ? Wenn nicht, ist die Idee, es in verschiedene Klassen aufzuteilen, eine Klasse für jeden Codierungstyp (derzeit 'onehot' und 'ordinal' Codierung).

  2. Wenn wir in mehrere Klassen aufteilen, könnten wir (idealerweise?) OneHotEncoder für die 'onehot'-Codierung verwenden, aber diese Klasse existiert bereits. Integrieren wir also die neue 'onehot'-Codierung (die Strings unterstützt und unterschiedliche Parameter hat) in die vorhandene OneHotEncoder-Klasse? Oder wählen wir einen anderen Namen? (zB DummyEncoder)

  3. Wenn wir uns für die Integration in den bestehenden OneHotEncoder entscheiden, sind wir mit den folgenden Konsequenzen in Ordnung: Wir verwerfen eine Reihe der Schlüsselwörter/Attribute von OneHotEncoder und ein bestimmter Anwendungsfall (automatisches Ignorieren ungesehener Werte innerhalb des Bereichs der sichtbaren Werte) ist nicht möglich nach Ablauf der Laufzeit nicht mehr.

Der Großteil der obigen Diskussion drehte sich um Frage 3 (die komplexen Details zur Integration von CategoricalEncoder(encoding='onehot') in OneHotEncoder). Aber lassen Sie uns zuerst eine Entscheidung für die ersten 2 Fragen treffen.

Der andere Faktor ist für mich, dass jeder den aktuellen Auto-Modus für in hält
OneHotEncoder ist seltsam. seine Implementierung, die coo in csr umwandelt, ist auch
seltsam. es verdient eine Neugestaltung. und den Leuten zu sagen, "wenn du einen heißen willst
Zeichenfolgen codieren, gehen Sie stattdessen zu CategoricalEncoder" ist umständlich, weil OHE
ist schon für kategorische gedacht...

hrm. Ich denke, wir haben OneHotEncoder beibehalten, weil es effizienter ist, wenn es verwendet werden kann ... Idealerweise würden wir all die seltsamen Verhaltensweisen loswerden. Ich wollte es irgendwie ablehnen, aber dann haben wir es nicht...

Ich wollte es irgendwie ablehnen, aber dann haben wir es nicht...

In meiner POC-PR (https://github.com/scikit-learn/scikit-learn/pull/10523) habe ich fast alles von OneHotEncoder außer seinem Namen veraltet ...

Es ist nicht viel effizienter. Und wenn LabelEncoder schnelle Pfade für ints hätte
im Bereich [0, n_values-1], wenn gerechtfertigt, wäre das gut genug.

@amueller , sind Sie von dem Problem überzeugt, dass wir je nach Codierung letztendlich unterschiedliche zusätzliche Parameter (z.

Ich werde versuchen, mir das in den Frühlingsferien in zwei Wochen anzuschauen, ok? Ich bin mir nicht sicher, ob ich vorher noch Zeit habe :-/

Ich hoffe, dies ist nicht der falsche Ort, um zu fragen, aber was macht die aktuelle Implementierung mit Tabellen, die innerhalb einer Spalte gemischt kategorial und nicht kategorial sind? Nehmen wir das Beispiel von https://github.com/pandas-dev/pandas/issues/17418

Betrachten Sie den Datenrahmen df = pd.DataFrame([{'apple': 1, 'pear':'a', 'carrot': 1}, {'apple':'a', 'pear':2, 'carrot':3}, {'apple': 2, 'pear':3, 'carrot':1}, {'apple': 3, 'pear':'b', 'carrot': 1}, {'apple': 4, 'pear':4, 'carrot': 1}]) der gleich ist:

  apple  carrot pear
0     1       1    a
1     a       3    2
2     2       1    3
3     3       1    b
4     4       1    4

DictVectorizer bietet genau das, was ich in diesem Fall brauche.

    from sklearn.feature_extraction import DictVectorizer
    enc = DictVectorizer(sparse = False)
    enc.fit_transform(df.to_dict(orient='r'))

Das gibt:

array([[ 1.,  0.,  1.,  0.,  1.,  0.],
       [ 0.,  1.,  3.,  2.,  0.,  0.],
       [ 2.,  0.,  1.,  3.,  0.,  0.],
       [ 3.,  0.,  1.,  0.,  0.,  1.],
       [ 4.,  0.,  1.,  4.,  0.,  0.]])

Wir können die Funktionsnamen der Spalten sehen mit:

    enc.feature_names_
    ['apple', 'apple=a', 'carrot', 'pear', 'pear=a', 'pear=b']

Es wäre toll, wenn der neue CategoricalEncoder eine Option dazu hätte.

Ich glaube nicht, dass wir so einen gemischten Fall behandeln wollen

Das ist eine Schande. Ein einfacher Unterfall ist, dass eine Spalte numerisch ist, aber einige fehlende Werte aufweist. Eine einfache Lösung besteht darin, die NaNs in leere Strings umzuwandeln und dann DictVectorizer wie in meinem obigen Beispiel zu verwenden. Dies erzeugt effektiv eine neue Funktion für den Fall, dass der Wert fehlt, lässt aber die numerischen Werte ansonsten unverändert. Ich habe festgestellt, dass dies eine sehr nützliche Technik ist.

Wird der neue CategoricalEncoder in der Lage sein, etwas Ähnliches zu tun?

Wir haben in Erwägung gezogen, Benutzern zu erlauben, NaN als separate Kategorie zu behandeln
oder ähnliches. aber das ist nicht dasselbe wie mit willkürlichen numerischen Werten umzugehen wie
anders als Saiten.

Das klingt gut.

Sie haben Recht, es gibt zwei Anwendungsfälle. Lassen Sie mich ein bestimmtes Beispiel erläutern, in dem es für mich hilfreich war, numerische Werte als unterschiedlich von Zeichenfolgen zu behandeln. Vielleicht gibt es eine bessere Lösung.

Angenommen, Sie haben ein ganzzahliges numerisches Merkmal, das einen großen Wertebereich annimmt. Sie vermuten jedoch, dass bei einigen kleinen Werten der genaue Wert von Bedeutung ist. Bei größeren Werten vermuten Sie, dass dies nicht der Fall ist. Eine einfache Möglichkeit besteht darin, alle kleinen Werte in Zeichenfolgen zu konvertieren, DictVectorizer wie oben beschrieben auszuführen und dann die Merkmalsauswahl durchzuführen oder einfach Ihren bevorzugten Klassifikator direkt zu verwenden.

Sie verwenden es also für eine nichtlineare Diskretisierung? Die nächste Ausgabe ist
wahrscheinlich einen Diskretisierer mit fester Breite, aber nach einem Protokoll
transform oder eine Quantil-Transformation, es sollte sich ähnlich verhalten wie Sie
wollen... Aber die Log-Transformation könnte in Ihrer Einstellung allein ausreichen.

Am 25. Februar 2018 um 18:10 Uhr schrieb lesshaste [email protected] :

Das klingt gut.

Sie haben Recht, es gibt zwei Anwendungsfälle. Lassen Sie mich ein bestimmtes Beispiel erklären
wo es sinnvoll war, numerische Werte als unterschiedlich von Strings zu behandeln
Für mich. Vielleicht gibt es eine bessere Lösung.

Angenommen, Sie haben ein ganzzahliges numerisches Merkmal, das einen großen Bereich von
Werte. Sie vermuten jedoch, dass bei einigen kleinen Werten der genaue Wert
ist wichtig. Bei größeren Werten vermuten Sie, dass dies nicht der Fall ist. Eine einfache
Was Sie tun müssen, ist, alle kleinen Werte in Zeichenfolgen zu konvertieren, führen Sie DictVectorizer aus
wie oben und führen Sie dann die Funktionsauswahl durch oder verwenden Sie einfach Ihren Favoriten
Klassifizierer direkt.


Sie erhalten dies, weil Sie erwähnt wurden.
Antworten Sie direkt auf diese E-Mail und zeigen Sie sie auf GitHub an
https://github.com/scikit-learn/scikit-learn/issues/10521#issuecomment-368288727 ,
oder den Thread stumm schalten
https://github.com/notifications/unsubscribe-auth/AAEz60cmjwlDVKGyXc6oPyIC9oLbptSgks5tYQdvgaJpZM4RpUE8
.

@jnothman Ja in gewisser Weise, außer mit einer Wendung. Sagen wir, ich vermute, dass einige der Werte von 1...1024 sinnvoll sind. Das heißt, 22 zeigt etwas Bestimmtes an, das sich stark von 21 oder 23 unterscheidet. Das Erstellen von Protokollen hilft hier nicht. Aber ich möchte alle Werte über 1024 so numerisch belassen, da ich denke, dass diese spezifischen Werte nicht viel bedeuten.

Es hört sich so an, als wüssten Sie zu viel über Ihre Variable für ein Generikum
verwandeln, um das zu sein, was Sie brauchen.

Am 25. Februar 2018 um 20:37 Uhr schrieb lesshaste [email protected] :

@jnothman https://github.com/jnothman Ja in gewisser Weise, außer mit a
Twist. Sagen wir, ich vermute, dass einige der Werte von 1...1024 sinnvoll sind.
Das heißt 22 zeigt etwas Bestimmtes an, das sich stark von 21 unterscheidet oder

  1. Protokolle zu erstellen hilft hier nicht. Aber ich möchte alle Werte stehen lassen
    1024 so numerisch, wie ich glaube, dass diese spezifischen Werte nicht viel bedeuten.


Sie erhalten dies, weil Sie erwähnt wurden.
Antworten Sie direkt auf diese E-Mail und zeigen Sie sie auf GitHub an
https://github.com/scikit-learn/scikit-learn/issues/10521#issuecomment-368295895 ,
oder den Thread stumm schalten
https://github.com/notifications/unsubscribe-auth/AAEz65bOdVB6k7rCAcgLBYz_NslxXWV0ks5tYSnggaJpZM4RpUE8
.

@jnothman Um es etwas klarer zu sagen, ich weiß nicht, dass 22 signifikant ist. Ich vermute nur, dass einige Werte sind, aber ich weiß nicht, welche oder wie viele es sind. Ich habe festgestellt, dass die Methode "in eine Zeichenfolge konvertieren" und dann die DictVectorizer-Methode sehr nützlich ist, um herauszufinden, welche dies sind.

@lesshaste Zum Thema NaNs als separate Kategorie siehe https://github.com/scikit-learn/scikit-learn/issues/10465
Wenn Sie die spezifische nichtlineare Diskretisierung oder gemischte numerische/String-Codierung weiter diskutieren möchten, können Sie gerne ein neues Thema eröffnen. Aber ich möchte mich hier auf das ursprüngliche Thema konzentrieren, dh die Benennung und Gliederung in verschiedene Klassen des CategoricalEncoder/OneHotEncoder.

Ich werde versuchen, mir das in den Frühlingsferien in zwei Wochen anzuschauen, ok? Ich bin mir nicht sicher, ob ich vorher noch Zeit habe :-/

@amueller das ist in Ordnung. Ich werde die nächsten zwei Wochen keine Zeit haben, an der PR zu arbeiten, die dadurch sowieso blockiert wird. Danach sollte ich auch wieder Zeit haben, daran zu arbeiten.

@amueller hattest du zeit, dir das mal anzuschauen?

@amueller sind Sie damit

Entschuldigung für die Abwesenheit. Scheint in Ordnung zu sein, aber können Sie mir vielleicht zwei Wochen geben, damit ich es tatsächlich überprüfen kann? Vielen Dank!

@amueller kein Problem, bei mir das gleiche :-)
Aber ich plane jetzt, mir das nochmal anzuschauen. Wenn Sie also einen Blick darauf werfen könnten, wäre das willkommen. Ich habe noch einiges an der PR zu erledigen (https://github.com/scikit-learn/scikit-learn/pull/10523), also überprüfe das noch nicht im Detail (du kannst es dir ansehen, um eine Idee zu haben von dem, was wir jedoch vorschlagen).
Ich denke, die Hauptfrage, die ich beantwortet sehen möchte, bevor ich viel Zeit investiere, ist, ob Sie mit der Aufteilung von CategoricalEncoder in mehrere Klassen einverstanden sind, und in diesem Fall, wenn Sie mit der Wiederverwendung von OneHotEncoder einverstanden sind (was bedeutet, einige seiner aktuellen (seltsamen) Funktionen ablehnen). Diese Fragen sind zusammengefasst in https://github.com/scikit-learn/scikit-learn/issues/10521#issuecomment -363851328 und https://github.com/scikit-learn/scikit-learn/issues/10521#issuecomment -364802471.

(und wenn wir uns da einig sind, gibt es noch viel zu diskutieren über die tatsächliche Umsetzung in der PR :))

Ich habe die PR https://github.com/scikit-learn/scikit-learn/pull/10523 aktualisiert, bereit zur Überprüfung

Ich sage vorsichtig, ich bin zurück ;)

IMHO das Wichtigste ist eine universelle API (dh Parameter und Verhaltensmuster) für alle Encoder, die wir besprechen

PS https://github.com/scikit-learn-contrib/categorical-encoding ?

Im category_encoders Paket haben alle Encoder ein cols Argument, ähnlich dem categorical_features im alten OneHotEncoder (obwohl es nicht genau die gleiche Art von Werten akzeptiert). Siehe zB http://contrib.scikit-learn.org/categorical-encoding/onehot.html
Das hängt also mit der aktuellen Diskussion zusammen, die wir in https://github.com/scikit-learn/scikit-learn/pull/10523 über die Einstellung von categorical_features oder nicht.

Für den Rest denke ich, dass es nicht wirklich widersprüchliche Schlüsselwörter gibt (sie haben einige andere spezifisch für Datenrahmen, die wir an dieser Stelle nicht zu sklearn hinzufügen). Zumindest die Benennung für OneHotEncoder und OrdinalEncoder stimmt mit dem category_encoders Paket überein.

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen