<jupyter_start><jupyter_text>Plusieurs modèles, données disjointesOn cherche à prédire la note d'un vin mais on suppose que cette qualité est dépendante de la couleur et qu'il faudrait appliquer des modèles différents selon la couleur.<jupyter_code>%matplotlib inline
from jyquickhelper import add_notebook_menu
add_notebook_menu()<jupyter_output><empty_output><jupyter_text>données<jupyter_code>from papierstat.datasets import load_wines_dataset
df = load_wines_dataset()
X = df.drop(['quality', 'color'], axis=1)
y = df['quality']
color = df['color']
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test, color_train, color_test = train_test_split(X, y, color)<jupyter_output><empty_output><jupyter_text>version manuelle On cale d'abord un modèle linéaire sur toute la base.<jupyter_code>from sklearn.linear_model import LogisticRegression
clr = LogisticRegression()
clr.fit(X_train, y_train)
from sklearn.metrics import accuracy_score
accuracy_score(y_test, clr.predict(X_test))<jupyter_output><empty_output><jupyter_text>Et maintenant on essaye de caler un modèle différent selon les couleurs.<jupyter_code>X_train_white = X_train[color_train == "white"]
y_train_white = y_train[color_train == "white"]
X_train_red = X_train[color_train == "red"]
y_train_red = y_train[color_train == "red"]
clr_white = LogisticRegression()
clr_white.fit(X_train, y_train)
clr_red = LogisticRegression()
clr_red.fit(X_train_red, y_train_red)
X_test_white = X_test[color_test == "white"]
y_test_white = y_test[color_test == "white"]
X_test_red = X_test[color_test == "red"]
y_test_red = y_test[color_test == "red"]
acc_white = accuracy_score(y_test_white, clr_white.predict(X_test_white))
acc_red = accuracy_score(y_test_red, clr_red.predict(X_test_red))
acc_white, acc_red<jupyter_output><empty_output><jupyter_text>Il faut agréger pour obtenir les performances sur la base complète.<jupyter_code>acc2 = (acc_white * len(y_test_white) + acc_red * len(y_test_red)) / len(y_test)
acc2<jupyter_output><empty_output><jupyter_text>C'est un petit peu mieux mais l'ensemble est compliqué à mettre en place. Il serait plus simple d'automatiser pour pouvoir faire de la validation croisée.  AutomatisationAu début, j'avais pensé écrire une classe inspirée de l'API de [scikit-learn](http://www.xavierdupre.fr/app/ensae_teaching_cs/helpsphinx/notebooks/02_basic_of_machine_learning_with_scikit-learn.htmla-recap-on-scikit-learn-s-estimator-interface) qui apprend plusieurs modèles selon une catégorie indiquée lors de l'apprentissage et de la prédiction.<jupyter_code>class SkBaseLearnerCategory:
    
    def fit(X, y, cat):
        # ...
        pass
        
    def predict(X, cat):
        # ...
        pass<jupyter_output><empty_output><jupyter_text>Mais à moins de changer l'interface des [pipeline](https://github.com/scikit-learn/scikit-learn/blob/master/sklearn/pipeline.pyL290), il n'y aucun chance pour que cette classe puisse en faire partie. J'ai donc opté pour le design suivant même s'il ne me plaît que moyennement parce qu'il faut gérer plusieurs containers différents (numpy, pandas, ...)<jupyter_code>class SkBaseLearnerCategory:
    
    def __init__(cat="column"):
        self.cat_col = cat
        pass
    
    def fit(X, y, cat):
        X = X.drop(self.cat_col, axis=1)
        # ...
                
    def predict(X, cat):
        X = X.drop(self.cat_col, axis=1)
        # ...<jupyter_output><empty_output><jupyter_text>Bref, ça marche...<jupyter_code>from papierstat.mltricks import SkBaseLearnerCategory
import pandas
model = SkBaseLearnerCategory("color", LogisticRegression())
new_x_train = pandas.concat([X_train, color_train], axis=1)
model.fit(new_x_train, y_train)
new_x_test = pandas.concat([X_test, color_test], axis=1)
accuracy_score(y_test, model.predict(new_x_test))<jupyter_output><empty_output><jupyter_text>Exemple sur un jeu articificielComme ce n'est pas très probant... Essayons de vérifier que l'implémentation est correcte en fabriquant un jeu de données où cela doit marcher.<jupyter_code>import numpy
n = 20
colorart = numpy.array([0 for i in range(0,n)] + [1 for i in range(0,n)])
Xart = numpy.random.random(n*2)
Xart = Xart[:, numpy.newaxis]
Yart = Xart.ravel() + colorart + numpy.random.random(n*2)/10
import matplotlib.pyplot as plt
fig, ax = plt.subplots(1, 1, figsize=(3,3))
ax.plot(Xart, Yart, 'o')
ax.set_title("Nuage de points artificiel");<jupyter_output><empty_output><jupyter_text>On apprend une régression linéaire.<jupyter_code>from sklearn.linear_model import LinearRegression
reg = LinearRegression()
reg.fit(Xart, Yart)
pred = reg.predict(Xart)
fig, ax = plt.subplots(1, 1, figsize=(3,3))
ax.plot(Xart, Yart, 'o', label="true")
ax.plot(Xart, pred, 'o', label="prédiction")
ax.set_title("Nuage de points artificiel")
ax.legend();<jupyter_output><empty_output><jupyter_text>Le jeu a été construit pour échouer. Voyons avec les couleurs.<jupyter_code>new_x_art = pandas.DataFrame(dict(X=Xart.ravel(), color=colorart))
model = SkBaseLearnerCategory("color", LinearRegression())
model.fit(new_x_art, Yart)
pred2 = model.predict(new_x_art)
fig, ax = plt.subplots(1, 1, figsize=(3,3))
ax.plot(Xart, Yart, 'o', label="true")
ax.plot(Xart, pred2, 'o', label="prédiction")
ax.set_title("Nuage de points artificiel")
ax.legend();<jupyter_output><empty_output><jupyter_text>L'implémentation fonctionne. Donc cela n'ajoute rien de caler un modèle différent sur vins rouges et blancs s'il est linéaire. Essaysons avec un autre.<jupyter_code>from sklearn.tree import DecisionTreeClassifier

model = DecisionTreeClassifier()
model.fit(X_train, y_train)
acc1 = accuracy_score(y_test, model.predict(X_test))

model = SkBaseLearnerCategory("color", DecisionTreeClassifier())
model.fit(new_x_train, y_train)
acc2 = accuracy_score(y_test, model.predict(new_x_test))

acc1, acc2<jupyter_output><empty_output><jupyter_text>C'est légèrement mieux mais cela varie dès qu'on exécute plusieurs fois. A vérifier avec une validation croisée.<jupyter_code>from sklearn.ensemble import RandomForestClassifier

model = RandomForestClassifier()
model.fit(X_train, y_train)
acc1 = accuracy_score(y_test, model.predict(X_test))

model = SkBaseLearnerCategory("color", RandomForestClassifier())
model.fit(new_x_train, y_train)
acc2 = accuracy_score(y_test, model.predict(new_x_test))

acc1, acc2<jupyter_output><empty_output><jupyter_text>Ici cela n'apporte rien du tout.<jupyter_code>from sklearn import svm
from sklearn.datasets import make_classification
from sklearn.feature_selection import SelectKBest
from sklearn.feature_selection import f_regression
from sklearn.pipeline import Pipeline
# generate some data to play with
X, y = make_classification(n_informative=5, n_redundant=0, random_state=42)
# ANOVA SVM-C
anova_filter = SelectKBest(f_regression, k=5)
clf = svm.SVC(kernel='linear')
anova_svm = Pipeline([('anova', anova_filter), ('svc', clf)])    
anova_svm.fit(X, y)<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.11036068400632275, 'fraction_numerical': 0.007041241557695071, 'mean_word_length': 6.532467532467533, 'pattern_counts': {'":': 0, '<': 57, '<?xml version=': 0, '>': 57, 'https://': 1, 'lorem ipsum': 0, 'www.': 1, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '14', 'dir': 'jupyter-structured-clean-dedup', 'id': '626714', 'license': 'MIT', 'n_tokens_mistral': 2588, 'n_tokens_neox': 2555, 'n_words': 560, 'path': '_doc/notebooks/lectures/wines_color_linear.ipynb', 'repo_name': 'sdpython/papierstat', 'size': '6959'}